S3 backups
Back up storage volumes to any S3-compatible bucket, on a schedule or on demand, and restore them from the dashboard.
S3 destinations
A destination (/s3-destinations) is a named, reusable S3 target, defined
once and pointed at by as many volumes as you like, rather than retyping a
bucket and credentials per volume. Each one is a name, an endpoint URL, a
bucket, a region, an access key id and a secret access key (stored encrypted at
rest, same scheme as registry passwords). Anything S3-compatible works: AWS S3,
MinIO, Cloudflare R2, Backblaze B2, Wasabi, and so on, addressed path-style.
The page has the same search box and pager as every other list page. A destination can't be deleted out from under a volume without the volume simply losing its target, so a volume whose destination is gone reports "no destination" and its backups fail with a config error rather than silently doing nothing.
S3-compatible backups
Turned on per-volume from storage/[volumeId], off by default: pick a
destination, optionally a key prefix, and optionally a cron schedule. The
homerun worker tars the volume's contents and streams it, gzipped, to
<prefix/>volumeName-<timestamp>.tar.gz through a hand-rolled Signature V4
client (multipart upload in 16 MiB parts, archives up to about 160 GB, never a
whole archive in memory, no SDK).
Both volume kinds are backed up the same way. The volume (a bind mount's
host path or a Docker-managed volume) is mounted read-only into a short-lived
alpine helper container that is never started: the Docker daemon reads its
files out through its archive API, and the helper is removed straight after. A
restore downloads the archive to a temporary file first, so the volume (and any
services stopped for it) are only touched once the download succeeded.
Set a cron schedule alongside the destination to back up automatically; the scheduler mirrors the scheduled-redeploy shape (a 60-second tick, a due-check, a guard against double-firing in the same minute). Enabled schedules also show up on the Scheduling page alongside cron redeploys and cron jobs.
Backups, scheduled or from a "Run now" button, are queued and run in the
background (see the job queue), so the button
returns straight away and the run shows up in the history on /backups once it
starts. A failed backup is retried once.
By default nothing is quiesced: Homerun reads the volume as it is, while the services using it keep running, which is fine for files but can produce a torn copy of a database that's mid-write. Two per-volume options on the same form fix that:
- Stop services during the backup stops every running service that mounts the volume just before the tar, and starts them again as soon as it's done (before the upload), whether the tar worked or not. The services are down for the length of the tar.
- Pre-backup command runs a shell command (
/bin/sh -c) inside a service's running container before each backup, for examplepg_dump -U postgres -f /var/lib/postgresql/data/dump.sql appormysqldump ... > /var/lib/mysql/dump.sql. Write the dump into the volume being backed up so it ends up in the archive. Run it in picks which service's container it runs in; left on the default, it's the first running service that mounts the volume. The command runs while the service is still up (before any stop), a non-zero exit or a run longer than 15 minutes fails the backup with the tail of its output, and nothing is uploaded.
Backup history
/backups is one row per attempt across every volume, scheduled or manual,
backups and restores alike, with its kind, when it started and finished, whether
it succeeded, the size, and the error if it didn't (hover the kind for the
object key). It has a search box (matching volume name, object key or error), a
Kind filter (backup/restore), an Outcome filter (success/failed/running) and a
pager over the whole history, so a long-running instance with hundreds of past
runs can page all the way back through them instead of only ever seeing the
newest handful. The same "Run now" button is available here as on a volume's own
page.
Restoring a backup
A volume with a destination has a Restore panel on its own page. List
backups reads what's in the bucket for that volume (under its key prefix),
newest first with date and size, and Restore on one of them queues a restore
that downloads it and unpacks it back into the volume, for both volume kinds,
through the same kind of short-lived alpine helper container.
Two options sit above the list and apply to whichever backup you restore:
- Wipe the volume first deletes everything in the volume before unpacking, so files that weren't in the backup don't survive. Off, the restore unpacks over the volume: files in the archive replace the ones on disk and anything else is left alone.
- Stop services during the restore (on by default) stops every running service that mounts the volume for the wipe and unpack, and starts them again afterwards, even when the restore fails. Turn it off only if you've stopped them yourself or nothing writes to the volume, otherwise it can end up with half-old, half-new data.
Restores go through the job queue like backups,
so the button returns straight away. The download happens before anything is
stopped, so services are only down for the unpack. A restore shares the volume's
lock with backups (it never runs while the same volume is being backed up), is
never retried, and shows up in the run log on the volume's page and on
/backups as a Restore row.
You can still fetch a backup from the bucket yourself (aws s3 cp, rclone,
your provider's console) if you'd rather unpack it somewhere else.
This guide lives in the project repo: edit it there, and this page follows within a day.