Docker Backups
Back up the volume without stopping the container
Name your volumes and compose files. Each is archived through a throwaway helper container and streamed, with the config that describes the stack, into a bucket you own.
Backup Jobs
Automated backup schedules
postgres-main nightly
Last run 6h ago
web-01 snapshots
Last run 9h ago
api-prod volumes
Last run 4m ago
staging-db weekly
Last run 3d ago
A second container does the reading
A named volume lives wherever the daemon decided to put it — a location that is not stable across storage drivers, rootless installs or remote hosts. So we ask Docker for the volume rather than guessing at a path.
For each volume in the job, VPS Snaps starts a disposable alpine container with two mounts: your volume at /data, and a run-scoped staging directory at /backup. It tars one into the other and exits. The --rm means it deletes itself on the way out.
Your application container is never signalled, never paused and never restarted. It does not know the backup happened. That is what makes a nightly volume backup something you can actually leave switched on for a production stack.
Once every volume is archived and each compose file has been copied in beside them, a single tar czf - over the staging directory is uploaded by your server straight to your bucket, part by part — hashed as it goes, never passing through us. The staging directory is removed as soon as the upload completes.
Run once per volume, on your host
docker run --rm \
-v myapp_postgres:/data \
-v /tmp/vpssnaps-<runId>:/backup \
alpine tar czf /backup/myapp_postgres.tar.gz -C /data .A docker the SSH user can talk to
The job runs as whoever you connect as. That account needs the docker CLI on its PATH and permission to reach the daemon socket — root, or membership of the docker group.
The alpine image, once
The helper container is plain alpine. The first run pulls it if it is not already on the host; every run after that uses the cached image and pulls nothing.
Room in /tmp for the volumes
Per-volume tarballs are staged in a run-scoped directory under /tmp and deleted once the run finishes successfully. The combined archive is never staged — it is streamed as it is created. A run that fails partway can leave that staging directory behind on the server; it's small, and the next successful run for that job doesn't reuse or depend on it.
What one run actually leaves in your bucket
One object per run, with the pieces still separable inside it. You can restore a single volume without unpacking a stack you did not break.
Volumes stay in their own tarballs rather than being merged, so restoring myapp_uploads after a bad deploy does not mean touching your database volume at all.
Compose files are copied in with their path flattened — /opt/myapp/docker-compose.yml becomes _opt_myapp_docker-compose.yml — so two files with the same basename from different directories cannot collide.
The archive is ordinary gzipped tar. Any machine with tar on it can read the whole thing, today or in five years, with or without a VPS Snaps account.
docker-backup-<jobId>-2026-09-03T02-00-06-118Z.tar.gz
└── vpssnaps-<runId>/
├── myapp_postgres.tar.gz one tarball per named volume
├── myapp_uploads.tar.gz
├── _opt_myapp_docker-compose.yml path flattened, / becomes _
└── container-inspect.json when the job names containersFour things a Docker backup will not do for you
Container backups fail quietly when someone assumes they cover more than they do. Here is the boundary, drawn where the code draws it.
Bind mounts are not volumes
A service declaring - /opt/myapp/data:/data has no named volume for Docker to hand us. Nothing about that path appears in a Docker backup. Those directories belong in a file backup job, which tars paths on the host directly.
Images are not in the archive
We capture the compose file that pins your image tags, not the layers themselves. Recovery pulls from your registry the same way a fresh deploy does — which keeps archives small, and means a deleted upstream tag is your problem to solve before disaster, not after.
A live volume is a live filesystem
Nothing is paused. tar walks the volume while the container keeps writing, so a file that changes during the copy can be caught mid-write: not a snapshot of one instant, as a power cut would leave. Fine for uploads, caches and static content. Not what you want for a database server. SQLite is the exception: a volume holding SQLite databases is archived from the host, with each database copied by SQLite's own backup API first, so apps such as Uptime Kuma and Vaultwarden keep every committed write. That needs python3 or sqlite3 on the host and an SSH user that can read the volume's directory; otherwise the volume is archived through the helper as usual.
One failed volume fails the run
If any volume cannot be archived, the run fails and the error names the volume. That is deliberate: a green run always holds every volume you asked for, because a backup that quietly holds less than it claims is worse than one that failed. Fix the volume, or take it out of the job, and run it again.
Cover the rest with the right job type
A Docker job only ever sees named volumes — bind mounts and a database running inside a container both fall outside it, and neither overlaps with what a Docker job already captures, so pairing job types here doesn't risk archiving the same bytes twice.
Databases inside containers
Tarring a live Postgres volume copies files mid-write. Point a database backup job at the container's host and port instead and get a real dump.
Read moreBind-mounted directories
Host paths mounted straight into a container are ordinary directories. A file backup job archives them with tar, exclude patterns and all.
Read moreHosts with no inbound SSH
The Agent runs the same Docker job from inside your network over an outbound WebSocket, and uploads to your bucket itself.
Read moreBringing a stack back up
Restoring is the run played backwards, using the same alpine helper. Nothing here needs VPS Snaps to be reachable — or to still exist.
Unpack the outer archive
One object per run, holding everything that run collected. Unpacking gives you the run directory with each volume still in its own tarball, so you can restore one volume without touching the others.
aws s3 cp s3://acme-backups/9p2v6x4q/docker-backup-....tar.gz . tar xzf docker-backup-....tar.gzPour each volume back in
The restore is the backup run in reverse: create the empty named volume, then let a throwaway alpine container untar into it. The same trick, the same image, no daemon plugins.
docker volume create myapp_postgres docker run --rm -v myapp_postgres:/data \ -v "$PWD/vpssnaps-<runId>":/backup \ alpine tar xzf /backup/myapp_postgres.tar.gz -C /dataBring the stack up on the captured compose file
Rename the flattened compose file back to docker-compose.yml. Because it pins the same image tags the stack was running, compose pulls what it needs and starts services against volumes that are already full.
mv _opt_myapp_docker-compose.yml docker-compose.yml docker compose up -d
Worth rehearsing on a spare host before you need it. Restoring into a fresh VM proves three separate things at once: that the volumes unpack, that your registry still serves the tags your compose file pins, and that the stack starts against restored data. Test it on a quiet afternoon rather than discovering the answer at 3am.
Everything else it does
Storage, schedules, alerts, recovery and team access, in one place. Which plan each one starts on is on the pricing page.
- Bring your own storage
- Backups land in your own S3-compatible bucket — Backblaze B2, Wasabi, Cloudflare R2, or plain S3 — or in your own Google Drive, Box, Dropbox or OneDrive. You hold the keys and the data.
- Snapshots stay with your provider
- Provider snapshots are created through the provider's own API and never leave your account. We store the snapshot ID, not the image.
- Eight providers, one dashboard
- DigitalOcean, Hetzner, Vultr, Linode, AWS EC2, AWS Lightsail, Google Compute Engine and Microsoft Azure — scheduled and reviewed from the same place.
- A replacement server on demand
- Recover now builds a new server on your own DigitalOcean, Hetzner, Vultr, Linode, AWS EC2, AWS Lightsail, Azure or Google Compute Engine account, from its snapshot or from its backups, software included.
- Failover on any cloud
- When a server stops answering, its traffic moves to the recovery server: by reserved or floating IP where your cloud has one, or by your DNS on Cloudflare, which works on every cloud.
- Know it's down, with the way back
- Uptime monitors for sites, ports and scheduled jobs confirm an outage before alerting, name the server's newest backup, and publish a status page. On paid plans.
- Your DNS zones, backed up
- Every record in a Cloudflare zone, kept in your own storage as a BIND file too. A restore previews what differs and writes back only that.
- Schedules that fit your traffic
- Hourly, daily, weekly, monthly, or a fixed interval in minutes — anchored to your timezone, so a 02:00 job stays at 02:00 across a DST change.
- Retention that prunes itself
- Set how many days to keep. Older snapshots and archives are cleaned up after each successful run, so storage bills stay flat.
- Every run on record
- Status, duration, size and trigger, with each step it took in order and its warnings and errors kept in place — so a failure tells you which step broke.
- Checksummed on upload
- Every archive is hashed as it streams to your bucket and the checksum is stored with the run, so you can verify what landed.
- Run on demand
- Trigger any job by hand before a migration or a risky deploy, without touching its schedule or its retention window.
- Alerts on five channels
- Email plus Slack, Microsoft Teams, Google Chat, and Discord — on success, on failure, and on a job that missed its schedule entirely.
- Encrypted credentials
- SSH keys, database passwords, and storage secrets are sealed with AES-256-GCM before they touch the database.
- Team access with roles
- Invite your team into a shared workspace as owner, admin, or member, so backups outlive whoever set them up.

“We manage a mix of VPS instances, Docker workloads, and traditional Linux servers, so backups used to be scattered across several different processes. VPS Snaps brought all of that into one dashboard and made it much easier to see what ran, what failed, and what needs attention.”
Point a job at your volumes tonight.
Add a server, list the named volumes and compose paths, pick a schedule. Then read the first run log — it tells you exactly which volumes were archived and how big each one turned out to be.
No credit card required. Cancel anytime.