A reliable backup strategy is the only difference between a minor incident and total data loss for your self-hosted game servers and applications. This guide explains how to set up automated, encrypted backups for Docker containers using Restic, cron schedules, and S3-compatible cloud storage.
Running containers without offsite backups means a corrupted Minecraft world, a bad database migration, or a VPS disk failure will destroy your data permanently. We will walk through building a production-ready backup pipeline with deduplication, encryption, automated retention policies, and one-command restores. We also look at how M.A.F Cloud provides built-in snapshots and automated backups directly from the browser.
The 3-2-1 Backup Rule for Self-Hosted Servers
Before writing any backup script, understand the industry standard 3-2-1 backup principle:
**3 copies of your data**: the live production data, one local backup, and one offsite backup
**2 different media types**: for example, local SSD storage on your VPS and object storage in the cloud
**1 copy stored offsite**: a separate datacenter or cloud provider so a fire, hardware failure, or provider outage cannot destroy everything
A local copy on the same VPS disk is not a complete backup. If the VPS provider terminates the instance or the underlying disk fails, both your live container and your backup file vanish together. Offsite storage is mandatory.
Why Restic Beats Tar and Rsync for Docker Backups
Many developers start with simple shell scripts using tar and rsync. While functional, that approach has major drawbacks as data grows:
**1. Tar lacks deduplication.** If you back up a 10 GB Minecraft world every night with tar, you consume 70 GB per week, even if 99% of the world data never changed. Restic uses chunk-based deduplication: only modified blocks get uploaded, saving bandwidth and storage costs.
**2. Tar lacks built-in encryption.** Backing up sensitive configuration files, API tokens, or player databases to third-party cloud storage without client-side encryption is a security risk. Restic encrypts all data with AES-256 before it leaves your server.
**3. Restic treats backups as immutable snapshots.** You can view any point in time as a clean filesystem, restore single files without extracting an entire archive, and prune old snapshots safely.
**4. Native support for S3 and object storage.** Restic talks directly to AWS S3, Cloudflare R2, Backblaze B2, MinIO, or Wasabi without needing extra tools like rclone or aws-cli.
Step 1: Install and Initialize Restic
Install Restic on your Ubuntu or Debian VPS:
sudo apt update && sudo apt install -y restic
Set your S3 storage credentials as environment variables. We use Cloudflare R2 or Backblaze B2 as cost-effective S3-compatible targets:
export AWS_ACCESS_KEY_ID='your_access_key'
export AWS_SECRET_ACCESS_KEY='your_secret_key'
export RESTIC_REPOSITORY='s3:https://your-account-id.r2.cloudflarestorage.com/my-server-backups'
export RESTIC_PASSWORD='a-strong-random-encryption-password'
Initialize the repository. This creates the encryption key and repository structure:
restic init
Store the RESTIC_PASSWORD securely. If you lose this password, your encrypted backup data is permanently unrecoverable.
Step 2: Backing Up Docker Volumes Safely
A common mistake is copying Docker volume directories while the container is actively writing data. For databases (MySQL, Postgres) and game servers (Paper Minecraft, Palworld), a live copy can capture half-written files, resulting in corrupted backups.
### Safe Method A: Container Pause During Snapshot
For game servers like Minecraft, pause the container for the few seconds Restic takes to read modified chunks:
# Pause the container to flush disk writes
docker pause mc-paper-server
# Run Restic snapshot on the Docker volume directory
restic backup /var/lib/docker/volumes/mc-paper-data/_data --tag minecraft
# Unpause immediately
docker unpause mc-paper-server
Because Restic only reads new or modified chunks after the initial run, the pause window lasts only 2 to 5 seconds. Players rarely even notice a brief lag tick.
### Safe Method B: Database Dump First
For web applications running Postgres or MySQL, dump the database cleanly before snapshotting:
# Export a clean SQL dump
docker exec app-postgres pg_dump -U postgres mydatabase > /tmp/backup/db.sql
# Backup both the dump and application files
restic backup /tmp/backup/db.sql /var/lib/docker/volumes/app-uploads/_data --tag webapp
# Clean up temporary dump file
rm -f /tmp/backup/db.sql
Step 3: Complete Automated Backup Script with Cron
Create a dedicated backup script at /usr/local/bin/docker-backup.sh:
#!/usr/bin/env bash
set -euo pipefail
# Environment configuration
export AWS_ACCESS_KEY_ID='your_access_key'
export AWS_SECRET_ACCESS_KEY='your_secret_key'
export RESTIC_REPOSITORY='s3:https://your-account-id.r2.cloudflarestorage.com/my-server-backups'
export RESTIC_PASSWORD='your-strong-password'
LOG_FILE="/var/log/docker-backup.log"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Starting automated backup..." >> "$LOG_FILE"
# List of game containers to pause during backup
CONTAINERS=("mc-paper-server" "palworld-server")
for c in "${CONTAINERS[@]}"; do
if docker ps --format '{{.Names}}' | grep -q "^${c}$"; then
echo "Pausing container: $c" >> "$LOG_FILE"
docker pause "$c" || true
fi
done
# Run Restic backup on all Docker volumes
restic backup /var/lib/docker/volumes/ --tag automated-daily >> "$LOG_FILE" 2>&1
for c in "${CONTAINERS[@]}"; do
if docker ps --format '{{.Names}}' | grep -q "^${c}$"; then
echo "Unpausing container: $c" >> "$LOG_FILE"
docker unpause "$c" || true
fi
done
# Retention policy: keep last 7 daily, 4 weekly, 6 monthly snapshots
echo "Pruning old snapshots..." >> "$LOG_FILE"
restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune >> "$LOG_FILE" 2>&1
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Backup completed successfully." >> "$LOG_FILE"
Make the script executable and restrict permissions so only root can read credentials:
sudo chmod 700 /usr/local/bin/docker-backup.sh
Schedule it via root cron to run every night at 3:00 AM:
# Open crontab editor
sudo crontab -e
# Add this line at the bottom
0 3 * * * /usr/local/bin/docker-backup.sh > /dev/null 2>&1
Step 4: Verifying and Restoring Backups
A backup is only as good as your ability to restore it. Test your restore process regularly.
### View Existing Snapshots
restic snapshots
Output displays snapshot IDs, timestamps, tags, and directory paths for every point in time.
### Restore a Full Snapshot to a Specific Directory
# Restore the latest snapshot to a target directory
restic restore latest --target /tmp/restore-test
# Restore a specific snapshot by ID
restic restore 3a5f8b9c --target /tmp/restore-test
### Mount Backups as a Virtual Filesystem
One of Restic's best features is mounting snapshots via FUSE so you can browse them like ordinary folders and copy individual files:
mkdir -p /mnt/restic
restic mount /mnt/restic
# In another terminal, browse historical snapshots
ls -la /mnt/restic/snapshots/
How M.A.F Cloud Handles Backups Out of the Box
Setting up Restic, S3 buckets, cron scripts, and volume pausing manually gives you total control, but requires maintenance and testing. If you want automated backup protection without writing shell scripts, M.A.F Cloud builds this workflow directly into the panel.
When you enroll your VPS using the one-line agent installer:
curl -fsSL https://cexi.my.id/agent-install.sh | sudo MAFCLOUD_TOKEN=your_token MAFCLOUD_API=https://cexi.my.id bash
You get complete backup management from the web dashboard:
**One-click on-demand snapshots**: Take an instant snapshot of your server world, plugins, and configs before updating versions or testing new mods
**Automated backup schedules**: Set daily or weekly automated snapshot rules with automated pruning of older snapshots
**Clean restore in seconds**: Select any snapshot from the Backups tab and restore it with one click. The panel stops the container, restores volume data cleanly, and restarts the server automatically
**Crash protection + AI self-heal**: If a bad plugin or corruption crashes your server, M.A.F Cloud's crash auto-restart and circuit breaker recover the instance, with full rollback options available if the crash persists
**Downloadable backups**: Export snapshots directly to your local PC for offline cold storage
The Free tier gives you full backup capabilities for 1 server with 2 GB RAM, while Pro (RM 29.90/month) and Studio (RM 59.90/month) expand backup slots and storage limits for unlimited servers.
Summary Checklist for Production Backups
1. **Offsite destination**: S3 bucket, Cloudflare R2, or Backblaze B2 configured outside your main VPS provider
2. **Client-side encryption**: Strong encryption password stored in a secure password manager
3. **Clean state capture**: Containers paused or database dumped before taking snapshots
4. **Automated rotation**: Prune policy configured (keep-daily 7, keep-weekly 4, keep-monthly 6)
5. **Cron automation**: Scheduled during low-traffic hours (e.g. 03:00 local time)
6. **Quarterly restore test**: Actually restore a snapshot to a test container once every three months to verify integrity
With this setup, hardware failure or accidental deletions become routine five-minute recovery tasks rather than catastrophic events.