> ## Documentation Index
> Fetch the complete documentation index at: https://docs.shipways.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Backups

> Database dumps, kept somewhere other than the server they came from.

A dump sitting on the same disk as the database it came from survives a bad
migration and nothing else. So Shipways will not schedule a backup until you
have said where to put it.

## Destinations

Anything that speaks S3: AWS, Backblaze B2, Wasabi, Cloudflare R2, MinIO. Leave
the endpoint empty for AWS itself; give the endpoint URL for everything else.

A destination belongs to the organization, so one bucket can take the backups
of every database across every server.

Removing a destination also clears the schedule of any database pointing at it.
Leaving one with a schedule and nowhere to put the result would be a database
that looks backed up in the panel and is not — which is the exact state the
alerts exist to prevent.

## Scheduling

Per database: a destination, a **cron expression**, and how many to **keep**.

The expression is the ordinary five fields. `0 3 * * *` is three in the
morning. Shipways looks for due backups every hour, so an expression naming any
hour is honoured; naming a minute other than the hour will not run more often
than hourly.

Retention is a count of backups, not days, and old ones are dropped **only
after a new one has been checked**. A failing backup never takes the last
working one with it.

## What a backup run does

<Steps>
  <Step title="Dump">
    `mysqldump` or `pg_dump`, compressed, streamed straight to the destination.
  </Step>

  <Step title="Check what arrived">
    The part worth having. A dump that failed halfway, a disk that filled, a
    pipe that broke: all of them leave a file, and a file in a bucket looks
    exactly like a backup until somebody needs it. Anything too small to be a
    real dump is refused rather than recorded.
  </Step>

  <Step title="Prune">
    Down to what the database keeps.
  </Step>
</Steps>

Postgres dumps are taken without owner and grant statements, so a dump restores
into a database owned by whoever is restoring it rather than failing on a role
that does not exist on the machine it landed on.

## Restoring

**Restoring destroys whatever is in the target database.** It is the only thing
Shipways does that destroys data on purpose.

So it takes a copy of the target as it stands first, and **refuses to go on if
that copy fails**. Overwriting a database having failed to save it first is
precisely the thing worth refusing to do.

You can restore into a different database from the one the backup came from,
which is how a copy of production ends up in staging. It has to be on the same
server, because the dump is fetched and imported there.

Postgres restores stop at the first error. psql's own default is to carry on
and exit zero, which turns a half-restored database into a restore that
reported success.

## Alerts

Two [alerts](/alerts) come from here.

**Backup failed** — a run did not produce a usable dump.

**Backup overdue** — a database with a schedule has not had a successful backup
in **36 hours**. This is the one that catches a schedule that has quietly
stopped firing, which no individual run can report.
