On an unmanaged VPS, backups are your responsibility. A server can be lost to a bad command, a compromised application, or an OS reinstall, and off-server backups are the only reliable protection. This guide covers practical backup strategies from simple to automated.
The Golden Rule: Off-Server
A backup stored on the same VPS it protects is not a backup. Always copy backups to a different location: your local machine, another server, or object storage (S3-compatible providers, Backblaze B2, etc.).
What to Back Up
- Web files: typically
/var/www - Databases: dumped with the database's own tool (never just copy live database files)
- Configuration:
/etc/nginxor/etc/apache2,/etc/php, SSL certificates (/etc/letsencrypt), cron jobs (crontab -l) - Application data: uploads, Docker volumes, game world files, etc.
Level 1: Manual Backup (Simple, On-Demand)
On the server:
mysqldump -u root -p --all-databases > /root/all-databases.sql tar -czf /root/backup-$(date +%F).tar.gz /var/www /etc/nginx /etc/letsencrypt /root/all-databases.sql
Then pull it to your local machine:
scp root@YOUR_SERVER_IP:/root/backup-*.tar.gz .
Good before risky changes; not sufficient as your only strategy because it depends on you remembering to run it.
Level 2: Scheduled Backups with cron
Create /root/backup.sh:
#!/bin/bash set -e STAMP=$(date +%F) mysqldump -u root --all-databases > /root/db-$STAMP.sql tar -czf /root/backup-$STAMP.tar.gz /var/www /etc/nginx /root/db-$STAMP.sql # keep last 7 local dailies ls -1t /root/backup-*.tar.gz | tail -n +8 | xargs -r rm -- rm -f /root/db-$STAMP.sql
Make it executable and schedule it daily at 03:00:
chmod +x /root/backup.sh crontab -e # add: 0 3 * * * /root/backup.sh
Then sync the backups off-server from another machine, or push them (Level 3).
Level 3: Automated Off-Server Backups with restic (Recommended)
restic is a free, encrypted, deduplicating backup tool with native ARM64 and x86_64 builds. It supports SFTP and S3-compatible storage.
# install (Debian/Ubuntu) apt install restic -y # initialize a repository on any S3-compatible storage export AWS_ACCESS_KEY_ID=... export AWS_SECRET_ACCESS_KEY=... restic -r s3:https://S3_ENDPOINT/BUCKET_NAME init # run a backup restic -r s3:https://S3_ENDPOINT/BUCKET_NAME backup /var/www /etc/nginx # keep 7 daily, 4 weekly, 6 monthly snapshots restic -r s3:... forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune
Schedule the backup and prune commands via cron as in Level 2. Because restic deduplicates, daily backups are fast and small after the first run.
Test Your Restores
A backup you have never restored is a hope, not a plan. Periodically:
restic -r s3:... restore latest --target /tmp/restore-test
Confirm the files and a database import actually work. Ideally test on a fresh VPS or after an OS reinstall.
Database-Specific Notes
- MySQL/MariaDB:
mysqldumpfor small/medium databases;mariabackup/xtrabackupfor large, busy ones. - PostgreSQL:
pg_dumpall -U postgres > all.sql - Redis: copy the
dump.rdbfile after aBGSAVE.