How to Automate Docker Backups to AWS S3 or Cloudflare R2
tug.sh Team
Core Team
It is one of the most painful experiences in a developer's life: your server disk corrupts, a database file gets corrupted, or a project is accidentally deleted, and you realize your last backup is months old.
When running applications inside Docker on a VPS, backups can be tricky. Containers are ephemeral, but their data is persistent, stored in Docker Volumes or Bind Mounts.
In this guide, we'll cover how to automate secure, encrypted backups of your Docker databases and volumes directly to any S3-compatible storage (like AWS S3, Cloudflare R2, Backblaze B2, or MinIO) so you can sleep soundly at night.
What Needs to Be Backed Up?
When backing up a containerized application, you must distinguish between two types of data:
- Static Files & Uploads (Docker Volumes): Media files, configuration files, and assets. These can be backed up by archiving the volume directory.
- Databases (PostgreSQL, MySQL, Redis): Never copy raw database files while the database container is running. Doing so can result in corrupted backups. Instead, you must perform a database dump (e.g.,
pg_dumpormysqldump) to generate a clean SQL file, then archive that.
Step 1: Performing a Safe Database Dump
To back up a database like PostgreSQL safely without stopping the container, run the dump utility inside the container and pipe the output to your host system:
# PostgreSQL safe dump
docker exec -t postgres-container-name pg_dump -U db_user db_name > backup.sql
# MySQL safe dump
docker exec -t mysql-container-name mysqldump -u db_user -p'db_password' db_name > backup.sql
Once the SQL file is generated, you can compress it along with any static volumes into a single archive:
tar -czf project-backup.tar.gz backup.sql /var/lib/docker/volumes/project_uploads/_data
Step 2: Uploading the Archive to S3-Compatible Storage
To send the backup to an S3 bucket (or Cloudflare R2, which offers free egress bandwidth), you can use the official aws-cli or a lightweight tool like rclone.
Here is a simple bash script that dumps a database, archives a volume, uploads it using the AWS CLI, and deletes old backups to keep storage costs low:
#!/bin/bash
set -e
# Configuration
BACKUP_DIR="/tmp/backups"
BUCKET_NAME="my-tug-backups"
TIMESTAMP=$(date +"%Y%m%d%H%M%S")
FILENAME="project-backup-$TIMESTAMP.tar.gz"
mkdir -p "$BACKUP_DIR"
# 1. Dump database
docker exec -t my-db pg_dump -U postgres my_app > "$BACKUP_DIR/db.sql"
# 2. Compress DB dump and volume folder
tar -czf "$BACKUP_DIR/$FILENAME" -C "$BACKUP_DIR" db.sql -C /var/lib/docker/volumes/my-app-data/ _data
# 3. Upload to S3-compatible storage (e.g. Cloudflare R2)
aws s3 cp "$BACKUP_DIR/$FILENAME" "s3://$BUCKET_NAME/$FILENAME" --endpoint-url https://<account_id>.r2.cloudflarestorage.com
# 4. Clean up local files
rm -rf "$BACKUP_DIR"
echo "Backup $FILENAME successfully uploaded!"
Save this script on your VPS, make it executable (chmod +x backup.sh), and configure a cron job to run it daily:
crontab -e
# Runs the backup script every night at 2:00 AM
0 2 * * * /path/to/backup.sh
The Easy Way: Auto-Backups with tug.sh
While writing shell scripts and managing cron jobs works, it becomes tedious when managing multiple servers.
With tug.sh, automated backups are built directly into your control center:
- Connect your S3 storage: Enter your Access Key, Secret Key, and Endpoint URL once in the Storage Settings.
- Enable Backups on any project: Simply toggle the "Backups" switch on your project or database page.
- Set Retention: Define how many backups you want to keep (e.g., keep the last 14 backups).
The tug.sh agent will automatically handle database pausing, consistent dumps, file archiving, secure upload, and automated cleanup of older backups according to your retention policy. No scripts or crontab setup required!
Conclusion
Regardless of which method you choose — writing custom scripts or using a built-in tool like tug.sh — the best backup is the one that happens automatically. Test your backup restoration process at least once to ensure your archives are valid, and protect your projects from unexpected VPS disasters.