Docker data persistence is a critical skill. Learn how to automate reliable backups of your container volumes using Restic and Cron on Linux.

If you’ve ever accidentally wiped a production database container, you know the sinking feeling that follows. I learned this the hard way years ago when I deleted a volume directory thinking it was empty; I spent about four hours recovering data from a manual, out-of-date archive. Since then, I’ve moved away from "manual copies" to a consistent strategy for managing Docker data persistence using Restic.
Restic is a modern, fast, and secure backup program. It handles deduplication and encryption natively, which is a massive upgrade over simple tarballs. When you're running apps on a single VPS—much like the setup I described in my guide on deploying a side project on a single cheap VPS reliably—you need a solution that doesn't bloat your system or require a complex Kubernetes cluster.
We first tried simple rsync scripts to a secondary drive. It worked until the disk filled up because we were storing full copies every night. Switching to Restic saved us roughly 60% in storage costs because it only stores the chunks that have changed.
Before you start, make sure you understand how your containers handle state. If you haven't mastered the basics of container lifecycles, I recommend reviewing Docker for app developers: A mental model that sticks to ensure your volumes are mounted correctly.
For this setup, we assume your Docker volumes live in the standard /var/lib/docker/volumes/ path.
sudo apt install restic (or download the latest binary from their GitHub).Bashrestic -r /srv/my-backups init
RESTIC_PASSWORD as an environment variable to allow non-interactive runs.The most reliable way to handle data persistence backups is to keep the logic outside the container. If the container is crashing or stuck in a restart loop, your backup job should still be able to read the volume on the host filesystem.
Create a shell script at /usr/local/bin/backup-volumes.sh:
Bash#!/bin/bash export RESTIC_REPOSITORY="/srv/my-backups" export RESTIC_PASSWORD="your-secure-password" # Stop the containers if they write to disk constantly (Optional but recommended) docker stop my-db-container # Run the backup restic backup /var/lib/docker/volumes/my-app-data/_data # Start them back up docker start my-db-container # Prune old snapshots to save space restic forget --keep-daily 7 --keep-weekly 4 --prune
Make it executable with chmod +x /usr/local/bin/backup-volumes.sh. Now, add it to your crontab (sudo crontab -e) to run at 3 AM daily:
CRON0 3 * * * /usr/local/bin/backup-volumes.sh >> /var/log/backup.log 2>&1
When dealing with Linux system administration, permissions are your biggest hurdle. The Restic process needs read access to the Docker volumes directory. I prefer running the cron job as root to avoid permission denied errors, but ensure the backup repository folder itself is owned by a restricted user if you’re concerned about security.
Also, don't ignore your logs. If your backup fails, you want to know before you actually need the data. I usually combine this with a simple health check script that verifies the last snapshot date and alerts me if it's older than 26 hours.
The script above stops the container before backing up. This is the safest way to ensure data integrity for SQLite or PostgreSQL. If you cannot afford downtime, you have two options:
pg_dump) inside a container and back up the resulting SQL file.Q: Can I backup to S3 instead of a local disk?
Yes. Restic supports S3, Backblaze B2, and Azure Blob Storage natively. Just update your RESTIC_REPOSITORY string to point to your bucket (e.g., s3:s3.amazonaws.com/my-bucket-name).
Q: Does this handle container metadata?
No. This only backs up the volume data. If you lose your container configuration, you'll need to re-run your docker-compose.yml or docker run commands. I keep my compose files in a Git repository to solve this.
Q: How do I restore?
It's simple: restic -r /srv/my-backups restore latest --target /tmp/restore. You can then copy the data back to your volume directory.
I’m still tinkering with my pruning strategy. Sometimes I want to keep monthly backups for a year, but I worry about the repository size growing too quickly. For now, the keep-daily 7 --keep-weekly 4 logic is a good balance for my side projects.
Remember, a backup isn't a backup until you've successfully restored from it. Try doing a test restore once a quarter—you’ll be surprised at what you might have forgotten to include in your backup script.
Master blue-green deployment on a single VPS using Docker Compose and Traefik. Achieve zero-downtime releases without the complexity of Kubernetes clusters.