Skip to main content
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

1

Dump

mysqldump or pg_dump, compressed, streamed straight to the destination.
2

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.
3

Prune

Down to what the database keeps.
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 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.