Back to tutorials
Tutorial

VPS Backup Setup Guide Tutorial (2026): Automated Snapshots, Offsite Copies, and Restore Tests on Ubuntu

VPS backup setup guide tutorial for 2026: automate snapshots, offsite backups, and restore testing on Ubuntu with clear checklists.

By Anurag Singh
Updated on Jun 07, 2026
Category: Tutorial
Share article
VPS Backup Setup Guide Tutorial (2026): Automated Snapshots, Offsite Copies, and Restore Tests on Ubuntu

A backup you haven’t restored is just a wish. This VPS backup setup guide tutorial walks through a production-friendly routine for Ubuntu. You’ll set up fast local snapshots, encrypted offsite copies, and a repeatable restore test that proves you can recover.

The goal is simple. If your VPS gets popped, a disk fails, or an update breaks the stack, you can bring the site back quickly. You’ll also know exactly what you’re rolling back to.

What you’ll build in this VPS backup setup guide tutorial

  • Layer 1: Local snapshots for quick rollbacks (minutes).
  • Layer 2: Offsite backups for real disasters (datacenter loss, ransomware).
  • Layer 3: Restore testing so you catch missing files, bad permissions, and broken configs before an outage does.

This tutorial assumes Ubuntu Server 24.04 LTS or Ubuntu 26.04 LTS on a typical hosting VPS. Think Nginx or Apache, PHP, and WordPress (optional).

The same pattern works on dedicated servers. You’ll just have more storage and more components to protect.

Prerequisites (keep it boring and predictable)

  • A VPS running Ubuntu with root or sudo access.
  • Enough local disk to keep at least 2 snapshot points (often 10–30% overhead).
  • An offsite destination: another VPS, S3-compatible object storage, or a backup server reachable over SSH.
  • A maintenance window to run your first full backup and a restore test.

If you’re still tightening basic access controls, start with this VPS hardening tutorial and SSH lockdown first.

Backups don’t help much if an attacker can log in and wipe them.

Need a clean server to implement this end-to-end? A HostMyCode VPS is a solid base for automation. managed VPS hosting also works well if you want someone else handling daily maintenance.

Step 1: Decide what to back up (and what to exclude)

On most web VPS setups, the “must-have” list is smaller than people think. Focus on data you can’t recreate and configuration you don’t want to rebuild under pressure.

Include these paths on a typical web VPS

  • /var/www/ (site code + uploads)
  • /etc/ (Nginx/Apache, PHP-FPM pools, cron, SSL settings, system configs)
  • /home/ (if you host multiple users or use SFTP users)
  • /var/lib/ for app state where relevant (be selective)
  • /srv/ if you keep sites there
  • /var/log/ is optional; keep short history for forensics, but don’t bloat backups

Exclude these to avoid huge, noisy backups

  • /proc, /sys, /dev, /run
  • Package caches: /var/cache/apt/archives
  • Container layers if you use Docker heavily (back up your compose files and volumes instead)
  • Large temporary folders: /tmp, application temp directories

If you run WordPress

For WordPress, the “business data” usually comes down to three things:

  • wp-content/ (uploads, themes, plugins)
  • Your database (covered below)
  • wp-config.php and web server vhost configs

A staging workflow reduces risk before updates. If you don’t have one, use this WordPress staging tutorial to test changes before they hit production.

Step 2: Add fast local snapshots (quick rollback layer)

Snapshots aren’t your whole backup strategy. They’re your fastest “undo” button when a deployment, package upgrade, or config change goes wrong.

Many Ubuntu VPS images use LVM, so we’ll use LVM snapshots here. ZFS/Btrfs snapshots also work if that’s what you run.

2.1 Check if you’re on LVM

lsblk
sudo vgs
sudo lvs

If you see volume groups and logical volumes (for example ubuntu-vg and ubuntu-lv), you’re good to snapshot.

2.2 Create an LVM snapshot before risky changes

Pick a snapshot size based on write activity. For many web servers, 5G to 20G is a reasonable starting point.

# Example names; adapt to your VG/LV
sudo lvcreate -L 10G -s -n root-preupdate /dev/ubuntu-vg/ubuntu-lv
sudo lvs

Important: LVM snapshots grow as the origin volume changes. If the snapshot fills, it becomes invalid.

Keep snapshots short-lived (hours to days). Remove them once you’ve verified the change.

2.3 Remove snapshots after validation

sudo lvremove /dev/ubuntu-vg/root-preupdate

Snapshot pitfalls to avoid

  • Don’t keep a snapshot for weeks on a busy WordPress site—it will fill.
  • Snapshots are local. If the disk dies, they die too.
  • Snapshots don’t replace offsite backups. They mainly reduce downtime for simple rollbacks.

Step 3: Create an encrypted offsite backup with Restic (the real safety net)

Offsite backups cover the problems snapshots can’t. This includes accidental deletion, server compromise, filesystem corruption, and complete node loss.

In 2026, Restic is still a practical fit for VPS backups. You get deduplication and encryption by default. You also get automation that stays maintainable.

3.1 Install Restic

sudo apt update
sudo apt install -y restic

3.2 Choose an offsite target (SSH repo option)

A simple, reliable pattern is backing up over SSH to a second machine (a dedicated “backup VPS”). Host it on separate infrastructure if you can.

If you want a clean split from production, consider a second HostMyCode VPS dedicated to backups and monitoring.

On the backup server:

sudo adduser --disabled-password --gecos "" restic
sudo mkdir -p /backups/restic
sudo chown -R restic:restic /backups/restic

On the production VPS, create a dedicated SSH key:

sudo mkdir -p /root/.ssh
sudo ssh-keygen -t ed25519 -a 100 -f /root/.ssh/restic_ed25519 -C "restic-backup"

Copy the public key to the backup server (use the restic user):

sudo ssh-copy-id -i /root/.ssh/restic_ed25519.pub restic@BACKUP_SERVER_IP

Lock the key down on the backup server. Restrict it to Restic only.

Edit:

sudo nano /home/restic/.ssh/authorized_keys

Prefix the key with:

command="restic serve",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-ed25519 AAAA...

3.3 Initialize the Restic repository

On the production VPS:

export RESTIC_REPOSITORY="sftp:restic@BACKUP_SERVER_IP:/backups/restic"
export RESTIC_PASSWORD="use-a-long-random-password"
export RESTIC_SSH_COMMAND="ssh -i /root/.ssh/restic_ed25519"

restic init

Keep RESTIC_PASSWORD in a password manager. If you lose it, the backups are effectively gone.

3.4 Back up web data + configs (first run)

restic backup \
  /etc \
  /var/www \
  /home \
  --exclude /var/www/*/cache \
  --exclude /var/www/*/wp-content/cache \
  --exclude /var/www/*/wp-content/wflogs

The first run is the slow one. After that, Restic only ships deltas.

3.5 Add a database dump (without turning backups into a mess)

Even on “simple” WordPress installs, the database is often the only truly irreplaceable part.

A clean approach is to dump into a temporary directory, back up the dump, then delete older dumps locally.

Create a dump directory:

sudo mkdir -p /var/backups/db
sudo chmod 700 /var/backups/db

MariaDB/MySQL example (single site DB):

DB_NAME="wordpress"
DB_USER="wp_user"

mysqldump --single-transaction --quick --routines --triggers \
  -u "$DB_USER" -p "$DB_NAME" | gzip > /var/backups/db/${DB_NAME}-$(date +%F).sql.gz

Then back up the dump file and remove older local dumps:

restic backup /var/backups/db
find /var/backups/db -type f -name "*.sql.gz" -mtime +2 -delete

If you need a more complete email workflow, DNS checks, and reputation basics, keep this deliverability troubleshooting tutorial handy.

Email deliverability is a common “post-restore” surprise when IPs and DNS change.

Step 4: Automate backups with systemd timers (cleaner than cron)

Cron works, but systemd timers are easier to audit. You get consistent logging and clear status output. You also avoid “where did this job run from?” mysteries.

You’ll create a script, a service unit, and a timer.

4.1 Create the backup script

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

sudo nano /usr/local/sbin/restic-backup.sh

Paste (adjust repository, exclusions, and DB settings):

#!/usr/bin/env bash
set -euo pipefail

export RESTIC_REPOSITORY="sftp:restic@BACKUP_SERVER_IP:/backups/restic"
export RESTIC_PASSWORD="REPLACE_WITH_LONG_RANDOM_PASSWORD"
export RESTIC_SSH_COMMAND="ssh -i /root/.ssh/restic_ed25519"

# Optional: dump a DB before file backup
DUMP_DIR="/var/backups/db"
mkdir -p "$DUMP_DIR"
chmod 700 "$DUMP_DIR"

DB_NAME="wordpress"
DB_USER="wp_user"

# Use a protected credentials file instead of putting DB passwords in the script.
# Example: /root/.my.cnf with 600 perms.
mysqldump --single-transaction --quick --routines --triggers \
  -u "$DB_USER" "$DB_NAME" | gzip > "$DUMP_DIR/${DB_NAME}-$(date +%F).sql.gz"

# Run backup
restic backup /etc /var/www /home "$DUMP_DIR" \
  --exclude /var/www/*/cache \
  --exclude /var/www/*/wp-content/cache \
  --exclude /var/www/*/wp-content/wflogs

# Retention policy (tune to your needs)
restic forget --prune --keep-daily 7 --keep-weekly 4 --keep-monthly 6

# Cleanup local dumps
find "$DUMP_DIR" -type f -name "*.sql.gz" -mtime +2 -delete

Make it executable:

sudo chmod 700 /usr/local/sbin/restic-backup.sh

Tip: Put MySQL credentials in /root/.my.cnf (permissions 600). This avoids hardcoding passwords:

sudo nano /root/.my.cnf
sudo chmod 600 /root/.my.cnf
[client]
user=wp_user
password=YOUR_DB_PASSWORD

4.2 Create the systemd service

sudo nano /etc/systemd/system/restic-backup.service
[Unit]
Description=Restic backup job
Wants=network-online.target
After=network-online.target

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

4.3 Create the timer (daily)

sudo nano /etc/systemd/system/restic-backup.timer
[Unit]
Description=Run Restic backup daily

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

[Install]
WantedBy=timers.target

Enable and start:

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

4.4 Check logs and job status

sudo systemctl status restic-backup.timer
sudo journalctl -u restic-backup.service --since "today"

Step 5: Add a weekly restore test (this is where most setups fail)

Backups fail quietly. Permissions drift. A plugin starts writing giant cache directories.

Your exclude list can also catch files you actually need. A restore test surfaces that drift before you’re restoring during an outage.

5.1 Verify repository integrity

Run weekly (monthly at minimum):

export RESTIC_REPOSITORY="sftp:restic@BACKUP_SERVER_IP:/backups/restic"
export RESTIC_PASSWORD="..."
export RESTIC_SSH_COMMAND="ssh -i /root/.ssh/restic_ed25519"

restic check

5.2 Do a file-level restore into a temp directory

sudo mkdir -p /root/restore-test
sudo chmod 700 /root/restore-test

restic snapshots
# Pick the latest snapshot ID
restic restore latest --target /root/restore-test

Then check a few items that matter:

  • Your site files exist: /root/restore-test/var/www/...
  • Your web server configs exist: /root/restore-test/etc/nginx/ or /root/restore-test/etc/apache2/
  • Your database dump exists and is non-empty: ls -lh /root/restore-test/var/backups/db/

5.3 Test a database restore safely

Create a temporary database on a test server. You can also do it on the same VPS if you’re careful.

Example on the same machine:

mysql -u root -p -e "CREATE DATABASE restore_test;"

Restore from the latest dump:

zcat /root/restore-test/var/backups/db/wordpress-$(date +%F).sql.gz | mysql -u root -p restore_test

If the dump file name doesn’t match today’s date (common), pick the newest dump:

ls -1t /root/restore-test/var/backups/db/*.sql.gz | head -n 1

5.4 Clean up restore-test data

sudo rm -rf /root/restore-test
mysql -u root -p -e "DROP DATABASE restore_test;"

Step 6: Document your recovery plan (you’ll thank yourself later)

Write down the exact restore steps for your environment. Keep it short and specific.

Store it somewhere off the VPS (a password manager secure note is a good place).

Minimum viable recovery runbook

  • Where your Restic repo lives (hostname/IP, path).
  • Where the Restic password is stored.
  • SSH key location for backup access.
  • How to restore files (restic restore example you’ve tested).
  • How to restore DB (commands, credentials source).
  • DNS steps: TTL strategy and where to change records.

If your recovery plan includes moving to a new server, pair this with this near-zero downtime migration guide.

Backups help during migrations, but they aren’t the whole story. Plan for DNS timing, SSL re-issuance, and verification steps.

Step 7: Hardening the backup path (small changes, big payoff)

Your backups contain the same secrets and customer data your production server does. Treat the backup path like a first-class production system.

Checklist: secure backup operations

  • Use a dedicated SSH key for backups only (done above).
  • Restrict the key with command="restic serve" on the backup server.
  • Limit inbound SSH on the backup server to your production VPS IP.
  • Patch both machines and reboot for kernel updates in your maintenance cadence.
  • Firewall rules: allow only ports you need (usually 22 on the backup server, and 22/80/443 on the web server).

If you want a clean hosting ruleset, use this UFW firewall tutorial or, if you prefer explicit rules, this IPTables ruleset guide.

Step 8: Operational tuning: retention, bandwidth, and backup windows

Two issues show up over and over: retention that’s too short, and backups that run during peak traffic.

Both are easy to fix once you spot them.

Practical retention defaults for hosting

  • Daily: 7–14 snapshots
  • Weekly: 4–8 snapshots
  • Monthly: 6–12 snapshots

For WooCommerce or sites with frequent updates, keep more dailies. Storage is usually cheaper than losing a week of orders or content.

Keep backups from hurting site performance

  • Run backups off-peak (systemd timer at night).
  • Use Nice and I/O scheduling (already set in the service unit).
  • Exclude caches and logs that churn.
  • If your VPS is tight on CPU/RAM, consider upgrading plans rather than trying to tune around hard limits.

If you’re already operating close to the edge, upgrading to managed VPS hosting can be practical.

You get help with maintenance windows, performance checks, and recovery planning—not just compute.

Step 9: Quick diagnostics (when backups fail at 2 AM)

Restic can’t connect over SSH

  • Test the SSH command Restic will use:
ssh -i /root/.ssh/restic_ed25519 restic@BACKUP_SERVER_IP
  • Confirm the backup server firewall allows SSH from your VPS IP only.
  • Check /home/restic/.ssh/authorized_keys for the forced command syntax.

Timer runs but no backups appear

  • Inspect logs:
sudo journalctl -u restic-backup.service -n 200 --no-pager
  • Run the script manually to see interactive errors:
sudo /usr/local/sbin/restic-backup.sh

Backup size exploded overnight

  • List largest directories in your web root:
sudo du -xhd1 /var/www | sort -h
  • Common offenders: WordPress security plugin logs, caching directories, old staging copies, large media uploads.

Summary: a backup routine you can actually trust

You now have a layered setup: fast local snapshots for rollbacks, encrypted offsite backups for disasters, and a restore test that proves recoverability.

That’s the difference between “we take backups” and “we can recover.”

If you want a stable base for this setup, start with a HostMyCode VPS.

If you’d rather hand off routine maintenance while keeping admin control, HostMyCode managed VPS hosting fits how most production sites run in 2026.

If you’re building a backup plan for a production website, start with infrastructure you can rely on. HostMyCode offers VPS hosting that’s straightforward to administer, plus managed VPS hosting when you want help with patching, monitoring, and recovery planning.

FAQ

How often should I back up a WordPress VPS in 2026?

Daily is the baseline for most sites. If you publish frequently or run WooCommerce, add more frequent database dumps (every 6–12 hours) and keep at least 7 daily restore points.

Are VPS provider snapshots enough?

They help, but they’re not enough by themselves. Provider snapshots are usually stored in the same provider environment. Keep at least one offsite copy you control, encrypted, with a tested restore process.

What’s the biggest backup mistake on hosting servers?

Not testing restores. The second biggest is backing up everything (caches, logs, temp files) until backups become slow and unreliable.

Should I back up SSL certificates and private keys?

Yes. Back up /etc/letsencrypt/ (or your certificate directory) as part of /etc. If you rotate servers, having the private key avoids surprises with older clients and simplifies recovery.

How do I know my backup retention is “enough”?

Match retention to your risk window. If you might not notice a compromise for a week, keep at least 14 daily backups. If clients ask for content restored “from last month,” keep monthly points for 6–12 months.