Back up Docker volumes offsite
with restic and SFTP

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.

The right approach for Docker backups with restic

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.

Backing up bind mounts (simplest case)

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"

Backing up named volumes

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"

Database pre-backup hook

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

Full docker-compose + SFTP setup

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

Setting up the SFTP target

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.

Scheduling Docker backups automatically

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.

What to skip in a Docker backup

Some Docker artifacts are large, change constantly, and are cheap to recreate, making them poor candidates for offsite backup:

  • /var/lib/docker/overlay2:Container image layers. Never back these up - they are recreated by pulling images. Can be hundreds of GB.
  • Build caches:Docker buildkit caches in /var/lib/docker/buildkit. Exclude these.
  • Log files:Application logs stored in volumes. Exclude unless audit retention requires them.
  • Temporary files:Any /tmp, /run, or runtime state directories inside volumes.

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.

Verifying Docker volume restores

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.

FAQ

Common questions.

For bind mounts, point restic directly at the host directory. For named volumes, back up /var/lib/docker/volumes/ as root. For databases, dump to a file first then include the dump in the restic run.
For databases - yes, or dump first. For application data in bind mounts - usually no. For named volumes of stateless apps - live backup is fine. The key is consistency, not necessarily stopping.
Yes. The mazzolino/restic image wraps restic with cron scheduling and environment variable configuration. Mount your data directories read-only and provide your SFTP credentials.
Run 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.
Get started today

Start backing up Docker offsite today.

Private vault. Flat pricing. No egress fees when you restore.

No egress fees, cancel anytime, 7-day money-back guarantee