Back to tutorials
Tutorial

VPS backup strategy tutorial (2026): 3-2-1 backups with snapshots, Restic, and restore tests

VPS backup strategy tutorial for 2026: build 3-2-1 backups with snapshots, Restic, and proven restore tests.

By Anurag Singh
Updated on Aug 08, 2026
Category: Tutorial
Share article
VPS backup strategy tutorial (2026): 3-2-1 backups with snapshots, Restic, and restore tests

A backup you’ve never restored is just a theory. This VPS backup strategy tutorial walks through a practical 3-2-1 setup for production hosting. You’ll combine quick local snapshots, encrypted offsite backups, and restore tests you can run on a schedule.

The steps assume Ubuntu 24.04/26.04-style tooling (systemd, journald). The same approach works on Debian 12/13, AlmaLinux, and Rocky Linux. Expect small differences in paths and packages.

What you’re building: a 3-2-1 backup plan that works on a hosting VPS

On a hosting VPS (WordPress, PHP apps, mail, DNS, control panels), the safest plan isn’t “pick one backup tool.” Use layers. Each layer covers a different failure mode.

  • 1) Snapshots for fast rollbacks (minutes) after updates, config mistakes, or compromised sites.
  • 2) File-level backups (Restic) for clean restores of specific paths (e.g., /var/www, /etc) without replaying a whole disk image.
  • 3) Restore tests so you know it works before you’re restoring under pressure.

3-2-1 means: 3 copies of your data, on 2 different media/types, with 1 offsite. On a VPS, “different media” usually translates to “provider snapshot + object storage backup.”

Prerequisites and safety checks

Before you automate anything, confirm the basics. These issues can sabotage restores later.

  • You can SSH as a sudo user (not just root). You can also get console access from your provider if a firewall change locks you out.
  • You have enough disk space for temporary files (logs, dumps). If / is already at 90%, fix that first.
  • You know what you actually need to recover: site files, database, mail spools, DNS zones, TLS keys, and server config.

If you’re moving to a new server soon, migrate first. Then build backups on the new box.

This checklist pairs well with that workflow: hosting migration checklist.

Step 1 — Inventory what to back up (with a hosting-first checklist)

Backups often fail because something was never included. Start by listing the paths and settings you can’t afford to lose.

WordPress / PHP sites (Nginx or Apache)

  • Web roots: /var/www (or /home/*/public_html on cPanel-style layouts)
  • Nginx: /etc/nginx (include sites-available, sites-enabled, snippets)
  • Apache: /etc/apache2 (or /etc/httpd on RHEL-family)
  • PHP: /etc/php/*/fpm or /etc/php.ini depending on distro
  • TLS certificates (if not fully automated): /etc/letsencrypt

Databases (without turning this into a DB deep dive)

  • Logical dumps directory you control (you’ll create one): /var/backups/db
  • Credentials: prefer /root/.my.cnf (MySQL/MariaDB) or /root/.pgpass (Postgres) rather than embedding passwords in scripts

Email (if the VPS sends/receives mail)

  • Postfix: /etc/postfix
  • Dovecot: /etc/dovecot
  • Mail storage: often /var/mail and/or /var/vmail depending on setup
  • DKIM keys: depends on signer (e.g., /etc/opendkim/keys)

If you also manage deliverability, document rDNS/SPF/DKIM/DMARC. These aren’t “files,” but they matter during recovery.

Two references that help during rebuilds: VPS reverse DNS setup and DMARC setup tutorial.

Control panels (cPanel/WHM, Plesk, DirectAdmin)

Control panels usually ship with their own backup systems. Even so, capture what you’ll need to rebuild the same behavior later.

  • Panel configs and licensing notes
  • Custom templates and hooks
  • Any local scripts, cron jobs, and firewall rules

Step 2 — Set up fast rollback with VPS snapshots (and a sane schedule)

Snapshots are your “undo” button before and after risky changes. Use them for kernel updates, PHP upgrades, control panel updates, and big config edits.

They’re also a quick fix for accidental deletes.

Snapshot rules that prevent surprises

  • Snapshot before change: upgrades, migrations, firewall changes, panel updates.
  • Snapshot retention: keep it short. Think 3–10 snapshots, not months. Use offsite backups for history.
  • Know what snapshots miss: if you store data on external volumes or object storage, a disk snapshot won’t capture that.

If you want a snapshot workflow you can automate and validate, pair this tutorial with: VPS snapshot backup tutorial.

Hosting note: if you’re scaling beyond a single VPS and want provider-grade snapshots plus managed support for patching and rollbacks, consider managed VPS hosting from HostMyCode. It keeps backups aligned with maintenance windows.

Step 3 — Build encrypted offsite backups with Restic (S3-compatible)

Snapshots help you roll back a server. Offsite backups cover the uglier scenarios.

That includes corruption that’s been present for days, a compromised server you don’t want to restore “as-is,” or a provider-level incident.

Install Restic

On Ubuntu/Debian:

sudo apt update
sudo apt install -y restic

On AlmaLinux/Rocky, Restic may come via EPEL or vendor repos depending on version. If your repo version is old, use the official Restic release binary and pin it in your config management.

Create an S3 bucket and credentials

Use any S3-compatible storage (provider object storage, MinIO, etc.). You’ll need:

  • Bucket name (e.g., hmcode-backups-prod-1)
  • Endpoint (e.g., https://s3.example.com)
  • Access key + secret key
  • A strong Restic repository password

Store secrets in a root-only env file

Create /etc/restic/env:

sudo install -m 0700 -d /etc/restic
sudo nano /etc/restic/env

Example (adjust values):

export RESTIC_REPOSITORY="s3:https://s3.example.com/hmcode-backups-prod-1"
export AWS_ACCESS_KEY_ID="YOUR_ACCESS_KEY"
export AWS_SECRET_ACCESS_KEY="YOUR_SECRET_KEY"
export RESTIC_PASSWORD="USE_A_LONG_RANDOM_PASSWORD"
sudo chmod 0600 /etc/restic/env

Initialize the repository

sudo -i
source /etc/restic/env
restic init

Decide what to back up (paths that matter on a hosting VPS)

For a typical Nginx + PHP-FPM + WordPress VPS, these directories are a solid baseline:

  • /etc (configs)
  • /var/www (site files)
  • /var/backups (your database dumps and export artifacts)
  • /etc/letsencrypt (optional if you can re-issue quickly, still useful)

Skip transient directories that bloat repositories. Common examples include /var/cache, /tmp, and large log directories, unless you have a specific reason to keep them.

Step 4 — Add consistent database dumps (so file backups are actually restorable)

Restic backs up files, not a live database state. For hosting, a dependable pattern is simple.

Dump the database(s) into /var/backups/db. Then let Restic back up that directory.

MySQL/MariaDB dump script (safe defaults)

Create a dedicated directory:

sudo install -m 0700 -d /var/backups/db

Create /usr/local/sbin/backup-mysql.sh:

sudo nano /usr/local/sbin/backup-mysql.sh
#!/usr/bin/env bash
set -euo pipefail

BACKUP_DIR="/var/backups/db"
TS="$(date -u +%F_%H%M%S)"
OUT="$BACKUP_DIR/mysql_all_${TS}.sql.gz"

# Requires credentials available to root, ideally in /root/.my.cnf
# with [client] user=... password=...

mysqldump --single-transaction --routines --events --triggers --all-databases \
  | gzip -1 > "$OUT"

# Keep only 7 days of local dumps
find "$BACKUP_DIR" -type f -name "mysql_all_*.sql.gz" -mtime +7 -delete
sudo chmod 0750 /usr/local/sbin/backup-mysql.sh

Quick restore check (don’t skip this)

Pick one dump. Confirm it’s readable and not empty:

zcat /var/backups/db/mysql_all_*.sql.gz | head

You should see SQL header lines. If zcat errors, fix the dump process before you schedule anything.

Step 5 — Create the Restic backup job (with excludes and tagging)

Create /usr/local/sbin/restic-backup.sh:

sudo nano /usr/local/sbin/restic-backup.sh
#!/usr/bin/env bash
set -euo pipefail

source /etc/restic/env

HOST_TAG="$(hostname -f 2>/dev/null || hostname)"

restic backup \
  --host "$HOST_TAG" \
  --tag "prod" \
  --one-file-system \
  --exclude "/var/cache" \
  --exclude "/tmp" \
  --exclude "/var/tmp" \
  --exclude "/var/log" \
  /etc /var/www /var/backups

# Prune with a practical retention policy
restic forget \
  --host "$HOST_TAG" \
  --tag "prod" \
  --keep-daily 7 \
  --keep-weekly 4 \
  --keep-monthly 6 \
  --prune

# Lightweight integrity check (does not read the whole repo)
restic check --read-data-subset=1/50
sudo chmod 0750 /usr/local/sbin/restic-backup.sh

Retention depends on what you host and how quickly you notice problems. If you host client sites, longer monthly retention often pays off.

If storage is tight, shorten history and keep snapshots more frequent. For a structured way to pick numbers, use: server backup retention policy.

Step 6 — Schedule it with systemd timers (clean logs, predictable runs)

cron works, but systemd timers are easier to inspect on modern Linux. The order matters.

Run the DB dump first, then Restic.

Create a service unit

/etc/systemd/system/restic-backup.service:

sudo nano /etc/systemd/system/restic-backup.service
[Unit]
Description=Nightly Restic backup (includes DB dumps)
Wants=network-online.target
After=network-online.target

[Service]
Type=oneshot
ExecStart=/usr/local/sbin/backup-mysql.sh
ExecStart=/usr/local/sbin/restic-backup.sh
Nice=10
IOSchedulingClass=best-effort
IOSchedulingPriority=7

Create a timer

/etc/systemd/system/restic-backup.timer:

sudo nano /etc/systemd/system/restic-backup.timer
[Unit]
Description=Schedule nightly Restic backups

[Timer]
OnCalendar=*-*-* 02:15:00
RandomizedDelaySec=900
Persistent=true

[Install]
WantedBy=timers.target

Enable and test

sudo systemctl daemon-reload
sudo systemctl enable --now restic-backup.timer
sudo systemctl list-timers | grep restic

# Run once right now
sudo systemctl start restic-backup.service
sudo journalctl -u restic-backup.service -n 200 --no-pager

Step 7 — Run a real restore test (file restore + “new server” mindset)

A restore test should match how you’ll recover in real life. In practice, that usually means restoring onto a clean VPS.

If that’s not possible, restore into an isolated directory. Then verify the pieces you rely on.

Restore a single site directory to a temp path

sudo -i
source /etc/restic/env
mkdir -p /root/restore-test
restic snapshots

# Pick a snapshot ID and restore only one site
restic restore SNAPSHOT_ID --target /root/restore-test --include /var/www/example.com

Check ownership and a few key files:

ls -la /root/restore-test/var/www/example.com | head
test -f /root/restore-test/var/www/example.com/wp-config.php && echo OK

Prove the database dump can import (smoke test)

On a staging server (or a disposable container/VM), create an empty database and import one dump. Don’t do this on production unless you’re certain what you’re overwriting.

zcat /var/backups/db/mysql_all_*.sql.gz | head -n 50

If your dump includes CREATE DATABASE statements, import into a throwaway MySQL instance. The goal is basic confidence.

You want to know the dump is readable, complete, and not truncated.

Restore test frequency

  • After you change the backup scripts
  • After major OS/control panel updates
  • At least monthly for production hosting

Step 8 — Monitor failures before they become incidents

Backup jobs tend to “work” until they don’t. Common causes include bucket policy changes, expired credentials, full disks, or DNS breaking the endpoint.

You want to hear about failures the same day.

If you already collect uptime and log signals, add a rule for “restic-backup.service failed” and “no successful run in 24h.” This monitoring guide maps cleanly to backup jobs: server monitoring tutorial.

Quick diagnostics when a backup fails

  • Auth errors: re-check /etc/restic/env permissions and S3 keys.
  • Timeouts: confirm outbound firewall allows HTTPS (443). If you tightened rules recently, audit ports.
  • Disk full: prune old DB dumps in /var/backups/db; confirm logs aren’t exploding.
  • Huge backups: exclude caches and large uploads you can regenerate, or split repositories by workload.

Step 9 — Common pitfalls on hosting VPS backups (and how to avoid them)

These patterns commonly bite teams in day-to-day hosting operations.

“I snapshot daily, so I’m covered”

Snapshots are excellent for quick rollbacks. They won’t save you from long-lived corruption or a silent compromise.

They also won’t help if you restore the same malware back onto the server. Offsite, versioned backups give you clean points in time.

Backing up /var/log and wondering why storage costs spike

Logs are valuable, but they grow fast. Either exclude them (typical) or rotate aggressively and back up only security-relevant logs.

If you need long retention, keep logs in a separate pipeline and repository.

Database dumps with passwords hard-coded in scripts

It works until someone pastes the script into a ticket or chat. Use root-only config files (/root/.my.cnf) and keep permissions tight.

Not protecting backup credentials

Your offsite bucket keys are production secrets. Treat /etc/restic/env like an SSH private key.

Keep it 0600, root-only, and never copy it into shared locations.

Step 10 — A practical “backup day” runbook (copy/paste checklist)

  • Confirm last backup: systemctl status restic-backup.timer and journalctl -u restic-backup.service
  • List Restic snapshots: restic snapshots
  • Run an on-demand DB dump: sudo /usr/local/sbin/backup-mysql.sh
  • Restore one site directory to /root/restore-test and verify key files exist
  • Check retention: restic forget --dry-run ... before changing policy
  • Document where you’d restore: same VPS vs a clean replacement server

Summary: a backup strategy you can defend during an outage

The goal isn’t “we have backups.” The goal is answering three questions under pressure: what you can restore, how far back you can go, and how long recovery takes.

Snapshots handle fast rollback. Restic gives you encrypted offsite history. Restore tests keep the whole plan honest.

If you’d rather spend time on sites and customers instead of backup plumbing, HostMyCode can host and maintain your stack on HostMyCode VPS plans, or handle patching and operational guardrails with managed VPS hosting. Either way, keep the restore test on the calendar.

If you’re building a backup plan for client sites or production WordPress, start with a VPS that’s predictable under load and easy to recover. HostMyCode’s VPS hosting works well with Restic offsite backups and scripted restore tests.

If you want backups, patching, and rollback planning handled alongside you (instead of becoming another after-hours chore), choose managed VPS hosting and reduce day-to-day operational risk.

FAQ: VPS backup strategy tutorial questions

How often should I run Restic backups on a hosting VPS?

Nightly is a solid default for most sites. If you run WooCommerce, membership sites, or frequent content updates, consider every 4–6 hours plus nightly retention pruning.

Do I still need database dumps if I snapshot the VPS?

Yes, if you want clean recovery. Snapshots restore everything (including problems). Dumps let you rebuild a clean server and import only what you trust.

Should I back up Let’s Encrypt certificates?

You can usually re-issue quickly, but backing up /etc/letsencrypt can save time during emergency rebuilds. Protect private keys and keep repository encryption enabled.

What’s the simplest way to prove restores work?

Restore one site directory to a temporary path and verify critical files exist. Then import a recent DB dump into a staging environment. Do it monthly.

What if my backup job fails silently?

Don’t assume “no news” means success. Add an alert for the systemd unit failure and a “no successful run in 24 hours” check in your monitoring.