This guide shows how to back up and restore Docker images, volumes, and configuration with copy-paste examples. You will learn a simple workflow, a safe local pilot, validation checks, rollback planning, and common mistakes to avoid. The examples focus on Linux and Docker Compose, with notes that map to Kubernetes and CI/CD. By the end, you will have a repeatable approach you can run on a laptop and extend to production.
Workflow Overview
Use this end-to-end workflow for Docker backup and restore.
- Inventory what to protect
- Images and tags (avoid unpinned latest)
- Container definitions (Compose files, Dockerfiles, env files)
- Volumes and bind mounts that hold state
- Databases inside containers (use dumps, not raw file copies)
- Secrets and keys (store separately or encrypt)
- Quiesce or snapshot
- Pause writes: maintenance mode or short downtime
- For databases: use logical dumps (pg_dump, mysqldump) or consistent snapshots
- Back up
- Config: git-managed files, plus an immutable compose.lock export
- Images: docker save to a tar archive
- Volumes: tar via a temporary helper container
- Databases: dump to files stored with the backup
- Store and protect
- Add checksums (sha256)
- Encrypt at rest and in transit
- Keep offsite copies and versioned retention
- Validate
- Test a restore on a clean host or VM
- Verify container health, data checksums, and app endpoints
- Automate
- Shell scripts or Makefile
- Schedule via cron or CI/CD runners
- Document rollback
- Exact steps to return to a known-good backup
- Who decides, where the artifacts are, how to validate success
Practical Backups
The following example backs up a simple Nginx site that serves content from a named volume. Adapt the same pattern for other services.
Set up a demo service
# Network and volume
docker network create appnet
docker volume create webdata
# Run Nginx pinned to a tag
docker run -d --name web \
--network appnet -p 8080:80 \
-v webdata:/usr/share/nginx/html \
nginx:1.25
# Put sample content in the volume
docker run --rm -v webdata:/data alpine \
sh -c "echo 'Hello from backup demo' > /data/index.html"
Back up the volume with a helper container
mkdir -p backups
STAMP=$(date +%F_%H%M%S)
VOL_ARCHIVE=backups/webdata-$STAMP.tar.gz
VOL_SUM=backups/webdata-$STAMP.sha256
docker run --rm \
-v webdata:/data: ro \
-v "$(pwd)/backups":/backup \
alpine sh -c "tar czf /backup/$(basename $VOL_ARCHIVE) -C /data ."
sha256sum "$VOL_ARCHIVE" > "$VOL_SUM"
Back up the image (so you can restore without pulling)
IMG_ARCHIVE=backups/nginx-1.25-images-$STAMP.tar
docker pull nginx:1.25
docker save -o "$IMG_ARCHIVE" nginx:1.25
sha256sum "$IMG_ARCHIVE" > "${IMG_ARCHIVE}.sha256"
Back up container definition and Compose
# Save the running container inspect (captures ports, mounts, env)
docker inspect web > backups/web-$STAMP.inspect.json
# If you use Compose, lock and save the resolved config
# (works in a directory with compose.yaml)
# docker compose config > backups/compose-$STAMP.lock.yaml
Optional: database dumps (example for Postgres and MySQL)
# Postgres logical dump
# docker exec -e PGPASSWORD=$PGPASSWORD pg \
# pg_dump -U $PGUSER -h localhost -F c -f /tmp/db.dump $PGDATABASE
# docker cp pg:/tmp/db.dump backups/pg-$STAMP.dump
# MySQL logical dump
# docker exec mysql \
# sh -c 'mysqldump -u$MYSQL_USER -p$MYSQL_PASSWORD $MYSQL_DATABASE > /tmp/db.sql'
# docker cp mysql:/tmp/db.sql backups/mysql-$STAMP.sql
Practical Restore
Restore on a clean host or a throwaway VM first. Treat it as a disaster recovery drill.
Load images
# Verify and load the saved image
sha256sum -c backups/nginx-1.25-images-*.tar.sha256
IMG_TAR=$(ls -1 backups/nginx-1.25-images-*.tar | tail -n1)
docker load -i "$IMG_TAR"
Recreate and restore the volume
# Create the target volume
NEW_VOL=webdata
docker volume create $NEW_VOL
# Pick the latest archive and verify
sha256sum -c backups/webdata-*.sha256
VOL_ARCHIVE=$(ls -1 backups/webdata-*.tar.gz | tail -n1)
# Extract into the empty volume
docker run --rm \
-v $NEW_VOL:/data \
-v "$(pwd)/backups":/backup \
alpine sh -c "cd /data && tar xzf /backup/$(basename $VOL_ARCHIVE)"
Start the container and validate
docker run -d --name web-restore \
-p 8080:80 \
-v $NEW_VOL:/usr/share/nginx/html \
nginx:1.25
# Basic health check
curl -fsS http://localhost:8080 | tee /tmp/page.html
grep -q "Hello from backup demo" /tmp/page.html && echo OK || echo FAIL
If you use Compose, restore with the locked config
# In the project directory that holds compose-$STAMP.lock.yaml
# docker compose -f backups/compose-$STAMP.lock.yaml up -d
# Validate with docker compose ps and application checks
Local Pilot Plan
Start with a narrow, measurable pilot you can inspect locally.
Scope
- One service (Nginx as shown) with a named volume
- Backup artifacts: image tar, volume tar.gz, inspect JSON, checksums
Success criteria
- RTO under 5 minutes on a laptop
- Checksum validation passes for all artifacts
- Home page content matches expected text
Steps
- Implement the backup scripts from the examples
- Schedule a daily cron job to write into ./backups
- Run a restore on a fresh VM or Docker Desktop context
- Document exact commands, timings, and results
Next iteration
- Add a small Postgres container with pg_dump in the backup
- Move scripts behind Make targets (make backup, make restore)
- Store artifacts with encryption and offsite sync when available
Validation and Rollback
Validation checks
- Pre-backup: containers healthy (docker ps), app endpoint OK, disk space OK
- Artifact checks: sha256sum for all tars, list tar contents
- Post-restore: service responds, data present, logs clean of errors
Useful commands
# Pre-flight
docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}'
# Validate tar content without extracting
tar tzf backups/webdata-YYYYMMDD_HHMMSS.tar.gz | head -n 10
# Quick HTTP health
curl -fsSI http://localhost:8080 | grep -E 'HTTP/|Content-Type'
Rollback planning
- Keep at least 3 recent backup sets per service
- Tag sets with timestamp, environment, and a git commit id for the app
- To roll back: stop current containers, restore the prior known-good set, start, then validate
Docker rollback examples
# Stop the faulty release
docker compose down # or docker stop <containers>
# Restore previous artifacts (images and volumes) as in the restore section
# Start known-good images explicitly
docker run -d --name app \
-v appdata:/var/lib/app \
myorg/app:1.2.3
# For Compose, pin and start
docker compose -f compose-rollback.lock.yaml up -d
Tip: avoid unpinned latest tags. Use immutable tags or digests so rollback is deterministic.
Common Mistakes
Avoid these pitfalls
- Using docker export for stateful apps. It does not include named volumes. Use docker save for images and tar for volumes.
- Copying live database files without dumps. Use pg_dump or mysqldump, or stop the DB cleanly.
- Forgetting bind mounts that point to host paths. Include them or move data to named volumes.
- No checksums or encryption. Add sha256 and encrypt where required.
- Not testing restores. Schedule a periodic test on a clean host.
- Running backups inside the main app container. Use a short-lived helper container.
- Depending on latest tags. Pin to specific versions or digests.
- Skipping secrets planning. Keep secrets out of plain backups or encrypt them.
Notes for related platforms
- Docker Compose: commit compose files to git, create a compose.lock via docker compose config, and back it up.
- Kubernetes: the same concepts apply. Back up manifests and use volume snapshots or logical DB dumps. Test restores in a separate namespace.
- GitLab CI/CD: run backup scripts in a scheduled pipeline, store artifacts, and send them to your storage. Keep credentials secure.
Conclusion
You now have a practical workflow to back up and restore Docker images, volumes, and configs, plus a small pilot you can run today. Start with one service, add checksums, and practice a clean restore. Then expand to databases, use pinned versions, and schedule automation. As you scale to Compose stacks or Kubernetes, keep the same principles: inventory state, quiesce, back up, validate, and document rollback.