How to automate VPS backups with cron and restic
Step-by-step: set up automated, encrypted VPS backups with restic and cron — repository setup, a backup script, scheduling, retention, and monitoring for failures.
Install restic and create the repository
restic is a single binary and ships in most distro repositories (apt install restic, dnf install restic). Back up to object storage in a different failure domain than the VPS — any S3-compatible bucket. Put the secrets in a root-only environment file so they never appear in cron logs or process listings. If you lose RESTIC_PASSWORD the backups are unrecoverable, so store it in a password manager, not just on the VPS you are protecting.
sudo install -m 600 /dev/null /etc/restic/env sudo tee /etc/restic/env >/dev/null <<'EOF' export RESTIC_REPOSITORY="s3:https://s3.example.com/my-vps-backups" export RESTIC_PASSWORD="a-long-random-passphrase" export AWS_ACCESS_KEY_ID="..." export AWS_SECRET_ACCESS_KEY="..." EOF sudo bash -c 'source /etc/restic/env && restic init'
Write the backup script
The script sources the secrets, backs up the paths you care about, applies a retention policy, and prunes. Backing up /etc, your app directory, and a fresh database dump covers most single-server apps.
#!/usr/bin/env bash set -euo pipefail source /etc/restic/env # Optional: dump a database first so the backup is consistent # pg_dump -Fc -d myapp -f /var/backups/myapp.dump restic backup /etc /srv/myapp /var/backups --tag automated --host "$(hostname)" restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune
Schedule it with cron
Run it nightly from root's crontab (sudo crontab -e) and redirect output to a log so a failure leaves a trace. Prefer systemd? A timer with OnCalendar=*-*-* 02:30:00 gives the same schedule plus journalctl logging and systemctl list-timers visibility.
# Nightly at 02:30 30 2 * * * /usr/local/sbin/vps-backup >> /var/log/vps-backup.log 2>&1
Monitor failures and test restores
Cron keeps running a failing job without reporting it, so automation needs two safeguards. Monitor failures by sending the exit status to a healthcheck or ping service, or by alerting on a non-zero exit. And test restores on a schedule by restoring into a scratch directory and checking the contents.
source /etc/restic/env restic snapshots restic restore latest --target /tmp/restore-check
Common pitfalls
Most broken cron backups come from a few recurring mistakes.
- Secrets in the crontab — keep them in the root-only env file, not inline.
- No retention — without forget --prune the repository grows until the bucket bill hurts.
- Silent failures — a cron job with no monitoring is a backup you will discover is broken during a restore.
- Backing up to the same VPS — that violates 3-2-1; the off-site copy is the whole point.
When you outgrow cron
One server and one cron line is fine. Across several servers, the cron-and-grep approach stops scaling: you cannot easily see which hosts ran, which failed, or how old your newest recovery point is. That is what Rested provides on top of the same restic engine — schedules, run history, failure alerts, and restores across every server from one place, with your encryption key kept by you.
Related from Rested
Common questions
Frequently asked questions
How do I automate restic backups with cron?
Store your restic environment in a root-only file, write a small script that runs restic backup followed by restic forget --prune, and schedule it from root's crontab (for example nightly at 02:30), redirecting output to a log.
How do I keep restic credentials out of cron logs?
Put RESTIC_REPOSITORY, RESTIC_PASSWORD, and storage keys in a file such as /etc/restic/env with 0600 permissions, and source it inside the backup script rather than putting secrets in the crontab line.
Should I use cron or a systemd timer?
Either works. A systemd timer adds journalctl logging and systemctl list-timers visibility, which makes failures easier to notice than plain cron output.