Docker data backup cheatsheet.
Postgres
# Single DB
docker exec db pg_dump -U postgres myapp | gzip > backup-$(date +%F).sql.gz
# All databases
docker exec db pg_dumpall -U postgres | gzip > all-$(date +%F).sql.gz
# Custom format (parallel restore possible)
docker exec db pg_dump -U postgres -Fc myapp > backup-$(date +%F).dump
Postgres restore
gunzip -c backup.sql.gz | docker exec -i db psql -U postgres myapp
# Custom format
docker cp backup.dump db:/tmp/
docker exec db pg_restore -U postgres -d myapp /tmp/backup.dump
MySQL
docker exec db mysqldump --all-databases -uroot -p$PASS | gzip > backup.sql.gz
# Restore
gunzip -c backup.sql.gz | docker exec -i db mysql -uroot -p$PASS
Mongo
docker exec mongo mongodump --archive --gzip > backup-$(date +%F).archive.gz
# Restore
docker exec -i mongo mongorestore --archive --gzip < backup.archive.gz
Redis
# RDB snapshot
docker exec redis redis-cli BGSAVE
docker cp redis:/data/dump.rdb backup-$(date +%F).rdb
For AOF: copy appendonly.aof.
Named volume backup
docker run --rm \
-v pg_data:/data \
-v $(pwd):/backup \
alpine \
tar czf /backup/pg_data-$(date +%F).tar.gz -C /data .
Restore volume
# First create empty volume
docker volume create pg_data
# Extract into it
docker run --rm \
-v pg_data:/data \
-v $(pwd):/backup \
alpine \
sh -c "cd /data && tar xzf /backup/pg_data-2026-01-15.tar.gz"
Stop services before volume backup
docker compose stop web worker
docker compose stop db # if going for consistent snapshot
# backup volume
docker compose start
Or use DB-native dump (no stop needed).
Cron backup script
#!/bin/bash
# /usr/local/bin/backup.sh
set -e
BACKUP_DIR=/srv/backups
DATE=$(date +%F-%H%M)
RETENTION_DAYS=14
mkdir -p $BACKUP_DIR
# Postgres
docker exec db pg_dump -U postgres myapp | gzip > $BACKUP_DIR/db-$DATE.sql.gz
# Uploads volume
docker run --rm -v uploads:/data -v $BACKUP_DIR:/backup alpine \
tar czf /backup/uploads-$DATE.tar.gz -C /data .
# Prune old
find $BACKUP_DIR -name "*.gz" -mtime +$RETENTION_DAYS -delete
# Upload to S3
aws s3 sync $BACKUP_DIR s3://my-bucket/backups/
0 2 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1
Upload to S3 in same container
docker run --rm \
-v pg_data:/data \
-e AWS_ACCESS_KEY_ID -e AWS_SECRET_ACCESS_KEY \
amazon/aws-cli \
s3 cp s3://bucket/path /data/restore.tar.gz
restic / borgbackup
docker run --rm \
-v $(pwd):/data \
-e RESTIC_REPOSITORY=s3:s3.amazonaws.com/bucket/backup \
-e RESTIC_PASSWORD=xxx \
-e AWS_ACCESS_KEY_ID -e AWS_SECRET_ACCESS_KEY \
restic/restic \
backup /data
Deduplication + encryption + versioning.
Backup verification
# Periodically: try restoring to a test container
docker run --rm -e POSTGRES_PASSWORD=x -v $(pwd):/backup -d --name test postgres:16
sleep 5
gunzip -c /backup/db.sql.gz | docker exec -i test psql -U postgres
# Verify data
docker exec test psql -U postgres -c "SELECT count(*) FROM users"
docker rm -f test
Untested backups are not backups.
Snapshots (cloud volumes)
If your volumes are on EBS / GCP PD: use cloud snapshots — instant, copy-on-write, no I/O on source.
Point-in-time recovery (Postgres)
WAL archiving:
db:
image: postgres:16
command:
- "postgres"
- "-c"
- "archive_mode=on"
- "-c"
- "archive_command=cp %p /backup/wal/%f"
volumes:
- pg_data:/var/lib/postgresql/data
- wal_backup:/backup/wal
Plus periodic base backup.
DB replication (HA, not backup)
Postgres streaming replication via replica containers. Provides failover but isn’t backup — replicates corruption too.
Encrypt backups
docker exec db pg_dump -U postgres myapp \
| gzip \
| openssl enc -aes-256-cbc -salt -pass file:./.backup-key \
> backup.sql.gz.enc
# Restore
openssl dec -d -aes-256-cbc -pass file:./.backup-key < backup.sql.gz.enc \
| gunzip \
| docker exec -i db psql -U postgres myapp
Backup label / tagging
docker run --rm \
-v pg_data:/data \
-v $(pwd):/backup \
--label backup=true \
--label backup-date=$(date +%F) \
alpine tar czf /backup/data.tar.gz -C /data .
Common mistakes
- Backup script never tested (restore fails when needed).
- Live volume copy on mounted DB (inconsistent).
- Backups only on same disk (host failure = all lost).
- No retention → disk fills with old backups.
- Plain text backup → leaked DB == leaked everything.
Read this next
If you want my backup + restore + verify scripts, they’re at rajpoot.dev .
Building something AI-, backend-, or data-heavy and want a second pair of eyes? I do consulting and freelance work — see my projects and ways to reach me at rajpoot.dev .