Docker 89 🐳 Backup and Disaster Recovery Strategies for Docker Volumes and Container States
Docker’s persistence model creates a specific backup problem: named volumes and bind mounts hold the data that matters, but the container’s writable layer is disposable, and the image is reproducible from a registry. A backup strategy that copies the entire /var/lib/docker directory is fragile and often inconsistent; a strategy that stops the stack and tars the volumes is reliable but has downtime. The correct approach separates the backup targets—volume data, database dumps, orchestration configuration, and optionally container state—and applies the right tool to each. Restic and similar deduplicating, encrypted backup tools are the modern standard for volume data because they store backups off-host and provide incremental snapshots without the full-copy cost of tar .
Container state is a separate question. Docker’s checkpoint and restore feature, built on CRIU, can freeze a running container’s process tree to disk and restore it later, including the in-memory state. The feature is experimental, requires experimental: true in daemon.json, and has known limitations: seccomp support depends on the kernel, external terminals (docker run -t) break checkpointing, and TCP connections require additional CRIU configuration that Docker’s CLI does not expose . The Docker documentation marks it experimental, and the CRIU project recommends Podman for checkpoint/restore support. For disaster recovery, the practical recommendation is to treat checkpoint/restore as a development and debugging tool, not a production backup mechanism. Production recovery rebuilds containers from images and restores data from volume backups and database dumps.
This chapter covers the three backup layers—volume data, database dumps, and configuration—the restic-based encrypted off-host pattern, the tar-based temporary-container pattern for simpler setups, the consistency requirements for live databases, the checkpoint/restore feature and its limitations, and the disaster recovery runbook that ties the layers together. It assumes familiarity with Docker Compose and basic shell scripting.
Key point: Back up volume data and database dumps separately. A raw file copy of a live database volume is inconsistent; use the database’s native dump tool (pg_dump, mysqldump) for logical consistency, and restic for encrypted, deduplicated off-host storage. Container state checkpoint/restore is experimental and not a production disaster recovery mechanism.
Why Docker backup requires a layered strategy
The container is not the data. A container’s writable layer is deleted when the container is removed. The image is reproducible from the registry. The data that cannot be recreated is in the volumes, the bind mounts, the environment configuration, and the database state. A backup that captures the container but not the volume is a backup of nothing. A backup that captures the volume but not the docker-compose.yml is a backup that cannot be restored to a working state .
Live database volumes are inconsistent. A PostgreSQL data directory is not a set of independent files that can be copied in any order. The database maintains internal consistency through write-ahead logging and checkpointing. A tar of the data directory while the database is running produces a snapshot that may contain partially written pages, uncommitted transactions, and a write-ahead log that does not match the data files. The result is a backup that cannot be restored . The fix is either to stop the database before copying the files, or to use the database’s logical dump tool (pg_dump, mysqldump, mongodump), which produces a consistent snapshot of the logical state .
Off-host storage is the difference between a backup and a copy. A backup stored on the same host as the data is not a disaster recovery strategy. A disk failure, a filesystem corruption, or a ransomware encryption event destroys both the data and the backup. Restic solves this by writing encrypted, deduplicated blobs to an S3-compatible object store, a remote SFTP server, or a different physical host. The encryption means the storage provider cannot read the data, and the deduplication means incremental backups only upload the chunks that changed .
Configuration is part of the data. The docker-compose.yml, the .env files, the daemon.json, and any secrets or config files are the instructions for rebuilding the stack. Without them, restoring a volume is useless because there is no service to mount it in. The configuration should be version-controlled in Git and included in the backup set as a secondary measure .
The trade-off. Restic adds a dependency and a repository password that must be managed. A tar-based script is simpler but produces full copies every time, which is expensive for large volumes and slow to restore. The checkpoint/restore feature is the only way to capture in-memory state, but it is experimental and fails on containers with TCP connections or external terminals. The layered strategy is more work to set up, but each layer has a clear recovery path.
a. The restic volume backup pattern
Restic is a deduplicating, encrypted backup tool that reads a directory or a stream, splits it into content-defined chunks, and stores the encrypted chunks in a repository. The repository can be local, SFTP, S3, or any backend that has a restic implementation . The deduplication is what makes it suitable for daily volume backups: a 40 GB volume that changes by 200 MB per day produces snapshots that cost roughly 200 MB of new storage per day, not 40 GB .
The repository password is the only key. If it is lost, the data is unrecoverable. It should be stored in a password manager, not in the backup script or the repository itself .
# /root/.restic/env
RESTIC_REPOSITORY=s3:https://s3.example.com/backups/docker-volumes
RESTIC_PASSWORD_FILE=/root/.restic/password
AWS_ACCESS_KEY_ID=...
AWS_SECRET_ACCESS_KEY=...
For a volume that contains static files—uploads, configuration, assets—the volume can be backed up while the container runs, because there is no consistency requirement beyond the file-level copy. The volume’s host path is resolved with docker volume inspect and passed to restic backup .
# Static volume: back up the host path directly.
restic backup /var/lib/docker/volumes/appdata/_data \
--tag docker-volume --tag appdata
For a database volume, the backup must be a logical dump, not a file copy. The dump is streamed to restic through stdin, which avoids writing a temporary file to disk .
# PostgreSQL: logical dump streamed to restic.
docker exec -i postgres pg_dumpall -U postgres \
| restic backup --stdin --tag postgres-dump
The retention policy is applied with restic forget. A common policy keeps 7 daily, 4 weekly, and 6 monthly snapshots, and prunes the unreferenced chunks . The restic check command verifies the repository’s integrity, and the --read-data-subset=5% flag reads a sample of the data to detect corruption without reading the entire repository .
b. The tar-based temporary container pattern
The simpler alternative is a temporary Alpine container that mounts the volume read-only and the backup directory read-write, and runs tar to create a compressed archive . This pattern works on any Docker host, requires no additional tools, and produces a single file that can be copied off-host with rsync or scp.
# Back up a volume to a tar.gz file.
docker run --rm \
-v myapp_data:/source:ro \
-v /backups:/backup \
alpine \
tar czf /backup/myapp_data-$(date +%Y%m%d-%H%M%S).tar.gz -C /source .
The source volume is mounted read-only so the temporary container cannot modify it. The backup file is written to a host directory that is then synced to an off-host location. The pattern is identical for restoring: a temporary container mounts the target volume and the backup directory, and extracts the archive .
# Restore a volume from a tar.gz file.
docker run --rm \
-v myapp_data:/target \
-v /backups:/backup:ro \
alpine \
sh -c "tar xzf /backup/myapp_data-20260208-143022.tar.gz -C /target"
The tar pattern is best suited for small to medium volumes where the full-copy cost is acceptable, and for environments where adding restic or another backup tool is not practical. The limitation is that it produces full copies, not incremental snapshots, so the storage cost grows linearly with the backup count. A rotation script that keeps the last N archives mitigates the storage cost .
c. Checkpoint and restore: experimental container state capture
Docker’s checkpoint command uses CRIU to freeze a running container’s process tree and write it to disk. The docker checkpoint create command stops the container and writes the checkpoint; docker start --checkpoint restores it from the saved state . The feature must be enabled with experimental: true in /etc/docker/daemon.json, and CRIU must be installed on the host.
# Enable experimental features and restart Docker.
echo '{"experimental": true}' >> /etc/docker/daemon.json
systemctl restart docker
# Create a checkpoint.
docker checkpoint create mycontainer checkpoint1
# Restore from the checkpoint.
docker start --checkpoint checkpoint1 mycontainer
The use cases are development-oriented: rewinding a process to an earlier point, forensic debugging of a running process, and speeding up the start of slow-start applications . The limitations are significant. Containers with external terminals (docker run -t) cannot be checkpointed. TCP connections require the --tcp-established CRIU option, which is not exposed by Docker’s CLI and must be added to /etc/criu/runc.conf . The Docker documentation marks the feature experimental, and the CRIU project recommends Podman as the alternative for checkpoint/restore support .
For disaster recovery, checkpoint/restore is not the right tool. A checkpoint captures the process state at a moment in time, but it does not capture the volume data or the configuration. Restoring a checkpoint on a different host or after a kernel upgrade may fail because the checkpoint is tied to the kernel version and the CRIU version that created it. The production recovery path is to rebuild the container from the image and restore the volume data and database dumps.
Complete Example Session
# ============================================
# PART 1: RESTIC REPOSITORY INITIALIZATION
# ============================================
mkdir -p /root/.restic && chmod 700 /root/.restic
openssl rand -base64 32 > /root/.restic/password
chmod 600 /root/.restic/password
cat > /root/.restic/env <<'EOF'
RESTIC_REPOSITORY=s3:https://s3.example.com/backups/docker-volumes
RESTIC_PASSWORD_FILE=/root/.restic/password
AWS_ACCESS_KEY_ID=YOUR_KEY
AWS_SECRET_ACCESS_KEY=YOUR_SECRET
EOF
chmod 600 /root/.restic/env
set -a; . /root/.restic/env; set +a
restic init
# ============================================
# PART 2: STATIC VOLUME BACKUP
# ============================================
MOUNTPOINT=$(docker volume inspect --format '{{ .Mountpoint }}' appdata)
restic backup "$MOUNTPOINT" \
--tag docker-volume --tag appdata
# ============================================
# PART 3: DATABASE DUMP STREAMED TO RESTIC
# ============================================
docker exec -i postgres pg_dumpall -U postgres \
| restic backup --stdin --tag postgres-dump
# ============================================
# PART 4: RETENTION POLICY
# ============================================
restic forget \
--keep-daily 7 \
--keep-weekly 4 \
--keep-monthly 6 \
--prune
# ============================================
# PART 5: INTEGRITY CHECK
# ============================================
restic check --read-data-subset=5%
# ============================================
# PART 6: TAR-BASED VOLUME BACKUP
# ============================================
docker run --rm \
-v appdata:/source:ro \
-v /backups:/backup \
alpine \
tar czf /backup/appdata-$(date +%Y%m%d).tar.gz -C /source .
# ============================================
# PART 7: TAR-BASED RESTORE
# ============================================
docker volume create appdata_restored
docker run --rm \
-v appdata_restored:/target \
-v /backups:/backup:ro \
alpine \
tar xzf /backup/appdata-20260208.tar.gz -C /target
# ============================================
# PART 8: CONFIGURATION BACKUP
# ============================================
tar czf /backups/config-$(date +%Y%m%d).tar.gz \
/srv/app/docker-compose.yml \
/srv/app/.env \
/etc/docker/daemon.json
# ============================================
# PART 9: CHECKPOINT (EXPERIMENTAL)
# ============================================
# echo '{"experimental": true}' >> /etc/docker/daemon.json
# systemctl restart docker
# docker checkpoint create mycontainer cp1
# docker start --checkpoint cp1 mycontainer
# ============================================
# PART 10: THE COMPLETE FLOW
# ============================================
# 1. Restic repository on off-host S3.
# 2. Static volumes: restic backup of host path.
# 3. Databases: pg_dump | restic backup --stdin.
# 4. Retention: forget --keep-daily/weekly/monthly.
# 5. Config: tar of compose, env, daemon.json.
# 6. Checkpoint: experimental, not for DR.
Quick Reference
Backup Targets
| Target | Method | Consistency |
|---|---|---|
| Static volume | Restic or tar | File-level |
| Database volume | Native dump to restic stdin | Logical |
| Configuration | Tar or Git | File-level |
| Container state | Checkpoint (experimental) | Process-level |
Restic Commands
| Command | Purpose |
|---|---|
restic init | Initialize repository |
restic backup <path> | Back up directory |
restic backup --stdin | Back up stream |
restic forget --prune | Apply retention |
restic check | Verify integrity |
restic restore <id> --target | Restore snapshot |
Database Dump Commands
| Database | Command |
|---|---|
| PostgreSQL | docker exec -i pg pg_dumpall -U postgres |
| MySQL/MariaDB | docker exec -i mysql mysqldump -u root -p --single-transaction |
| MongoDB | docker exec -i mongo mongodump --archive --gzip |
| Redis | docker exec redis redis-cli BGSAVE |
Checkpoint Limitations
| Limitation | Detail |
|---|---|
| Experimental | Requires experimental: true |
| External terminal | docker run -t fails |
| TCP connections | Requires /etc/criu/runc.conf edit |
| Kernel/CRIU version | Checkpoint tied to creation environment |
Best Practices
✅ Do This:
# Store restic password in a password manager. // ✅
# Lose it and the data is unrecoverable.
# Stream database dumps to restic. // ✅
docker exec -i pg pg_dumpall -U postgres | restic backup --stdin
# Back up configuration alongside data. // ✅
tar czf config.tar.gz docker-compose.yml .env daemon.json
# Use retention policy. // ✅
restic forget --keep-daily 7 --prune
# Verify backups periodically. // ✅
restic check --read-data-subset=5%
❌ Don’t Do This:
# Tar a live database volume. // ❌
# Inconsistent snapshot; restore may fail.
# Store backups only on the same host. // ❌
# Disk failure destroys data and backup.
# Rely on checkpoint for disaster recovery. // ❌
# Experimental; tied to kernel/CRIU version.
# Forget the repository password. // ❌
# Data is unrecoverable.
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
| Inconsistent database backup | Live file copy | Use native dump tool |
| Backup lost with host | Same-host storage | Off-host S3/SFTP |
| Restic password lost | Not stored separately | Password manager |
| Checkpoint fails | External terminal or TCP | Remove -t, configure CRIU |
| Config missing after restore | Not backed up | Include compose and env files |
Real-World Examples
1. Restic Init
restic init
2. Static Volume Backup
restic backup /var/lib/docker/volumes/appdata/_data --tag appdata
3. Database Dump to Restic
docker exec -i pg pg_dumpall -U postgres | restic backup --stdin
4. Retention
restic forget --keep-daily 7 --keep-weekly 4 --prune
5. Integrity Check
restic check --read-data-subset=5%
6. Tar Backup
docker run --rm -v vol:/src:ro -v /backup:/b alpine tar czf /b/vol.tar.gz -C /src .
7. Tar Restore
docker run --rm -v vol:/t -v /backup:/b:ro alpine tar xzf /b/vol.tar.gz -C /t
8. Config Backup
tar czf config.tar.gz docker-compose.yml .env
9. Checkpoint Create
docker checkpoint create mycontainer cp1
10. Checkpoint Restore
docker start --checkpoint cp1 mycontainer
Visual
Layered Backup Strategy
┌──────────────────────────────────────────────┐
│ BACKUP LAYERS │
│ │
│ Configuration: │
│ docker-compose.yml, .env, daemon.json │
│ → Git or tar │
│ │
│ Volume data (static): │
│ uploads, assets, config volumes │
│ → restic backup of host path │
│ │
│ Database state: │
│ PostgreSQL, MySQL, MongoDB │
│ → native dump | restic backup --stdin │
│ │
│ Container state (optional): │
│ process tree, memory │
│ → checkpoint (experimental) │
│ │
│ Each layer has a separate recovery path. │
│ │
└──────────────────────────────────────────────┘
Restic Off-Host Flow
┌──────────────────────────────────────────────┐
│ DOCKER HOST │
│ │
│ Volume → restic backup → encrypted chunks │
│ │ │ │
│ │ ▼ │
│ │ S3 / SFTP / NAS │
│ │ (off-host) │
│ │ │
│ Database → pg_dump → stdin → restic │
│ │
│ Password → password manager (not on host) │
│ │
│ Host failure does not affect backup. │
│ │
└──────────────────────────────────────────────┘
Tar Temporary Container
┌──────────────────────────────────────────────┐
│ TAR BACKUP │
│ │
│ Alpine container: │
│ /source → volume (read-only) │
│ /backup → host directory (read-write) │
│ │
│ tar czf /backup/vol.tar.gz -C /source . │
│ │
│ Result: one file on host. │
│ Copy off-host with rsync/scp. │
│ │
│ Restore: same pattern, reversed. │
│ │
└──────────────────────────────────────────────┘
Checkpoint Decision
┌──────────────────────────────────────────────┐
│ CHECKPOINT/RESTORE │
│ │
│ Use for: │
│ - Development: rewind process state │
│ - Debugging: freeze and inspect │
│ - Fast restart of slow-start apps │
│ │
│ Do NOT use for: │
│ - Disaster recovery │
│ - Cross-host migration │
│ - Production backup │
│ │
│ Limitations: experimental, kernel-tied, │
│ fails on TCP and external terminals. │
│ │
└──────────────────────────────────────────────┘
Summary
| Item | Value |
|---|---|
| Static volume backup | Restic or tar |
| Database backup | Native dump streamed to restic |
| Configuration backup | Tar or Git |
| Container state | Checkpoint (experimental, not for DR) |
| Restic repository | Off-host S3/SFTP, encrypted |
| Restic password | Password manager |
| Retention | restic forget --keep-daily/weekly/monthly |
| Integrity | restic check --read-data-subset=5% |
| Checkpoint requirement | experimental: true, CRIU installed |
| Checkpoint limitation | External terminal, TCP, kernel version |
Key takeaways:
- Back up volume data, database dumps, and configuration separately. A raw file copy of a live database volume is inconsistent; use the database’s native dump tool. The configuration is part of the data, and without it the volume cannot be mounted into a working service .
- Restic stores backups off-host with encryption and deduplication. The repository can be S3, SFTP, or a different host. The encryption means the storage provider cannot read the data, and the deduplication means incremental backups are cheap. The password is the only key, and losing it means losing the data .
- The tar temporary-container pattern is the simpler alternative. A read-only mount of the volume and a read-write mount of the backup directory, with
tarcreating a compressed archive. It works on any host but produces full copies, not incremental snapshots . - Checkpoint/restore is experimental and not a disaster recovery mechanism. It captures process state, not volume data or configuration. It fails on containers with external terminals, requires CRIU configuration for TCP connections, and is tied to the kernel and CRIU version that created it. The Docker documentation marks it experimental .
- Retention and verification are part of the backup. A backup that is never pruned fills the storage, and a backup that is never verified is a hope, not a backup.
restic forget --pruneapplies the retention policy, andrestic checkverifies the repository’s integrity . - The recovery runbook must be tested. A backup strategy that has never been restored is a strategy that has never been validated. The runbook should include the steps to rebuild the stack from configuration, restore the volume data, and restore the database dump.
Remember: Docker backup is a layered problem, and each layer has a different tool. The configuration goes in Git. The static volumes go through restic or tar to off-host storage. The databases go through their native dump tools, streamed to the backup repository. The container state is a development tool, not a recovery mechanism. The restic password goes in a password manager, not in the script. The retention policy keeps the storage bounded, and the verification step proves the backups are readable. The disaster recovery runbook ties the layers together, and the only way to know it works is to restore from it before the disaster.
Stop using slow, ad-bloated tool sites! 🤮
🔎 Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)
⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free
🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!