Docker volumes hold the data that makes your containers useful. This guide shows how to back them up with restic over SFTP - encrypted, deduplicated, and offsite. Includes bind mounts, named volumes, and database pre-backup hooks.
Docker data lives in a few places depending on how you set things up. Bind mounts are just host directories - restic can back them up directly. Named volumes live under /var/lib/docker/volumes/ on the host. Databases need special handling because backing up live database files produces inconsistent snapshots.
If your compose file uses bind mounts like ./data:/app/data, you can back up the host path directly:
restic backup /opt/appdata /opt/compose /etc \
--tag docker \
--exclude="*/logs" \
--exclude="*/tmp"
Named volumes live under /var/lib/docker/volumes/. You need root to read them.
# List your volumes
docker volume ls
# Back up all named volumes
sudo restic backup /var/lib/docker/volumes/ \
--tag docker-volumes \
--exclude="*/buildkit"
For Postgres or MySQL, dump to a file before the restic run. This is more reliable than backing up live database files.
#!/bin/bash
set -euo pipefail
source /etc/restic/env
# Dump Postgres
docker exec postgres pg_dumpall -U postgres > /tmp/pg_dump_$(date +%Y%m%d).sql
# Dump MySQL/MariaDB
docker exec mariadb mysqldump -u root -p"$MYSQL_ROOT_PASSWORD" --all-databases > /tmp/mysql_dump_$(date +%Y%m%d).sql
# Now back up everything including the dumps
restic backup /opt/appdata /tmp/pg_dump*.sql /tmp/mysql_dump*.sql \
--tag docker
# Clean up dumps
rm -f /tmp/pg_dump_*.sql /tmp/mysql_dump_*.sql
# Retention
restic forget --keep-daily 7 --keep-weekly 4 --prune
If you prefer running restic in a container, this compose snippet mounts your data directories and runs a nightly backup:
services:
restic:
image: mazzolino/restic:latest
hostname: docker-host
restart: unless-stopped
environment:
RUN_ON_STARTUP: "true"
BACKUP_CRON: "0 30 2 * * *"
RESTIC_REPOSITORY: sftp:vaultuser@vault.servercrate.net:22150:/data
RESTIC_PASSWORD: your-repository-password
RESTIC_BACKUP_SOURCES: /data
RESTIC_FORGET_ARGS: >-
--keep-daily 7 --keep-weekly 4 --keep-monthly 3 --prune
volumes:
- /opt/appdata:/data:ro
- ~/.ssh:/root/.ssh:ro
ServerCrate provides a private SFTP endpoint for Restic. Create a free account, get your vault connection string, and plug it in as RESTIC_REPOSITORY. No egress fees when you restore.
For host-level restic backups, use a systemd timer as described in the systemd timer guide. For containerized restic, the mazzolino/restic image handles cron scheduling internally via the BACKUP_CRON environment variable using standard cron syntax.
A critical consideration for Docker backup scheduling: if you are dumping databases before the backup, the dump and the backup need to run sequentially in the right order. In a host script this is straightforward. In a container-based approach, use a custom entrypoint script that runs the dump and then triggers the restic backup.
Some Docker artifacts are large, change constantly, and are cheap to recreate, making them poor candidates for offsite backup:
Focus on the data that would be painful to regenerate: database contents, uploaded user files, configuration that isn't in version control, and SSL certificates.
After restoring a Docker volume, test that the application actually comes up correctly with the restored data before considering the backup complete. For databases, restore the dump file to a test container and verify the schema and row counts look correct. This sounds obvious but is frequently skipped - and the restore is the only part of backup that actually matters. See the restic restore guide for full restore command reference.
restic restore latest --target /tmp/restore and copy the files back to the volume path. Or mount the snapshot with restic mount /mnt/restic and copy individual files.Private vault. Flat pricing. No egress fees when you restore.
No egress fees, cancel anytime, 7-day money-back guarantee