If your backups live on the same VPS as your data, a disk failure, a hacked server or an accidental reinstall takes both. Copying backups to cloud storage — S3-compatible object storage or another server in a different location — is the simplest way to make them survive.
Choose a destination
| Destination | Pros | Notes |
|---|---|---|
| S3-compatible object storage (AWS S3, Backblaze B2, Wasabi, Cloudflare R2, self-hosted MinIO/Garage) | Cheap per GB, durable, pay as you go | Check egress (download) fees for restores |
| Another VPS over SFTP | Full control, predictable cost | Put it in a different location; protect it as carefully as the source |
| Consumer cloud drives (Google Drive, OneDrive, Dropbox) via rclone | Often already paid for | API limits; less suited to large servers |
A second small server with plenty of disk makes a good target — for example a Storage VPS in another location. You can even run your own S3 service on it — see Garage distributed object storage.
Tool choice: restic or rclone?
- restic — a backup program: encrypted, deduplicated snapshots with retention policies. Best for server backups.
- rclone — a sync tool for 70+ storage providers. Great for copying files or already-made archives; can also encrypt with its
cryptremote.
This guide uses restic for the backups, and mentions rclone where useful.
1. Prepare the data
Databases must be dumped first, so you back up a consistent file rather than live database files:
mkdir -p /var/backups/db
mariadb-dump --all-databases --single-transaction | gzip > /var/backups/db/all.sql.gz
(PostgreSQL: pg_dumpall. See Debian backup and disaster recovery.)
2. Set up restic with S3 storage
apt install -y restic
cat > /root/.restic.env <<'EOF'
export RESTIC_REPOSITORY="s3:https://s3.example-provider.com/my-bucket/web1"
export AWS_ACCESS_KEY_ID="…"
export AWS_SECRET_ACCESS_KEY="…"
export RESTIC_PASSWORD_FILE="/root/.restic-pass"
EOF
chmod 600 /root/.restic.env
openssl rand -base64 32 > /root/.restic-pass && chmod 600 /root/.restic-pass
source /root/.restic.env
restic init
Save the restic password in your password manager. Without it, the backups can’t be restored.
Use an access key restricted to that bucket. If the provider supports object lock / immutability, enable it so a compromised server can’t delete old backups.
3. The backup script
/usr/local/bin/backup.sh:
#!/bin/bash
set -euo pipefail
source /root/.restic.env
mariadb-dump --all-databases --single-transaction | gzip > /var/backups/db/all.sql.gz
restic backup /etc /var/www /var/backups/db /home \
--exclude-caches --exclude '/var/www/*/cache'
restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune
# Optional: tell a monitoring service the backup succeeded
# curl -fsS -m 10 https://hc-ping.com/YOUR-UUID > /dev/null
chmod 700 /usr/local/bin/backup.sh
/usr/local/bin/backup.sh # first run
4. Schedule it
crontab -e
15 3 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1
(Or a systemd timer, which handles missed runs after downtime.)
5. Monitor
A backup that silently stopped weeks ago is a common surprise. Use a “dead man’s switch” monitor (Healthchecks.io or a self-hosted equivalent) that alerts you when the script doesn’t check in.
Weekly integrity check:
0 5 * * 0 . /root/.restic.env && restic check >> /var/log/backup.log 2>&1
6. Restore
source /root/.restic.env
restic snapshots
restic restore latest --target /tmp/restore --include /var/www/example.com
# or browse interactively:
mkdir /mnt/restic && restic mount /mnt/restic
Test a restore every month or two — ideally onto a fresh VPS.
Using rclone for simple file copies
To copy an existing backup folder to a cloud drive or bucket:
apt install -y rclone
rclone config # add a remote, e.g. "b2"
rclone sync /var/backups b2:my-bucket/web1 --transfers 8
Add a crypt remote on top to encrypt file contents and names.
Summary
Dump databases, back up with restic to object storage or another server, keep a retention policy, monitor every run and test restores. Related: automated VPS backups and VPS maintenance.