Back to tutorials
Tutorial

Snapshot Backup Tutorial: LVM Snapshots + Rsync Offsite Backups on Ubuntu VPS (2026)

Snapshot backup tutorial for Ubuntu VPS: LVM snapshots + rsync offsite with retention and restore checks.

By Anurag Singh
Updated on Jul 01, 2026
Category: Tutorial
Share article
Snapshot Backup Tutorial: LVM Snapshots + Rsync Offsite Backups on Ubuntu VPS (2026)

A backup you haven’t restored is just disk usage. This snapshot backup tutorial shows a repeatable way to take consistent backups from a live Ubuntu VPS using LVM snapshots. Then you’ll push them offsite with rsync, apply retention, and run a quick restore check.

This approach fits the main constraint most VPS owners have: the server stays online. You’ll “freeze” the filesystem long enough to copy a clean point-in-time view. You won’t need to stop Apache/Nginx or PHP-FPM.

By the end, you’ll have a script you can schedule and a short checklist you can hand off.

What you’ll build (and what it’s good for)

  • Consistent backups from a busy server: websites, configs, and app files copied from an LVM snapshot.
  • Offsite copy over SSH using rsync with sane defaults and optional bandwidth control.
  • Retention so daily/weekly copies age out automatically.
  • Restore verification to catch failures early by checking the backup for expected paths.

Best fit: a VPS or dedicated server running one or more sites (WordPress, Laravel, static sites, small apps). On shared hosting you typically don’t get LVM access, so you’ll need control-panel backups instead.

If you still need to pick a server, start with a HostMyCode VPS so you control the storage layout, SSH access, and automation. If you’d rather have someone else handle the OS and baseline security, managed VPS hosting is the cleaner option.

Prerequisites and assumptions

  • Ubuntu Server (22.04 LTS or 24.04 LTS are common in 2026).
  • Your main filesystem lives on LVM (or you can migrate to it). Confirm with lsblk and lvs.
  • An offsite target you can SSH into (a storage VPS, another server, or a backup box) with enough disk.
  • Root or sudo access on the source VPS.

If you don’t have a clear recovery process yet, write down a simple runbook before you automate anything. HostMyCode’s restore-first disaster recovery runbook is a good template.

Step 1: Confirm you’re on LVM (or decide what to snapshot)

Run these on the VPS:

lsblk -f
sudo pvs
sudo vgs
sudo lvs -a -o +devices

You’re looking for the logical volume backing your root filesystem. It’s often named ubuntu-vg/ubuntu-lv or vg0/root.

If / sits on a plain ext4 partition without LVM, you can’t use LVM snapshots. To use this method, you must change the disk layout.

Practical tip: If your database lives on the same volume, the snapshot is crash-consistent at the filesystem level. That often restores fine for small WordPress sites.

It is not a replacement for database-aware dumps on high-write systems. This tutorial stays focused on filesystem consistency without downtime.

Step 2: Prepare the offsite backup target (SSH user, key, directory)

On the backup server (destination), create a dedicated user and a directory structure. Example: store backups in /srv/backups/vps01.

sudo adduser --disabled-password --gecos "" backup
sudo mkdir -p /srv/backups/vps01
sudo chown -R backup:backup /srv/backups/vps01

On the source VPS, generate a dedicated SSH key used only for backups:

sudo -i
ssh-keygen -t ed25519 -a 64 -f /root/.ssh/id_ed25519_backup -C "vps01-backup"

Copy the public key to the destination:

ssh-copy-id -i /root/.ssh/id_ed25519_backup.pub backup@BACKUP_HOST

If you want an extra guardrail, restrict that key in authorized_keys on the destination. At a minimum, disable password SSH and keep the SSH service patched.

Step 3: Size your snapshot (don’t guess)

LVM snapshots use copy-on-write space. The snapshot must absorb writes that happen while rsync runs.

If the server writes 2–5 GB during the backup window, your snapshot needs at least that much, plus headroom. If you size it too small, the snapshot can invalidate mid-run.

To estimate, watch write rate for a minute during typical load:

sudo apt-get update
sudo apt-get install -y sysstat
sar -b 1 60

Check the wrtn totals and extrapolate for your backup duration. On many small hosting VPS setups, 10–20 GB is a safe starting point. Busy WooCommerce or log-heavy servers may need more.

Step 4: Create and mount an LVM snapshot (manual run)

Pick your LV. The example uses /dev/ubuntu-vg/ubuntu-lv. Adjust for your server.

sudo lvcreate -L 15G -s -n root_snap /dev/ubuntu-vg/ubuntu-lv

Create a mount point and mount it read-only:

sudo mkdir -p /mnt/lvm-snap
sudo mount -o ro,norecovery /dev/ubuntu-vg/root_snap /mnt/lvm-snap

If you’re on ext4, norecovery is usually harmless. XFS uses different mount options.

If the mount fails, stop and confirm the filesystem type with lsblk -f.

Pitfall: Don’t mount the snapshot read-write. You’ll lose the “frozen” view and create confusing drift during the copy.

Step 5: Rsync the snapshot to offsite storage (safe defaults)

Copy from the snapshot mount, not from /. The pattern below writes into a dated directory per run:

export BACKUP_HOST="backup.example.net"
export BACKUP_BASE="/srv/backups/vps01"
export TODAY="$(date +%F)"

sudo rsync -aHAX --numeric-ids --delete \
  --info=stats2,progress2 \
  --exclude="/mnt/*" \
  --exclude="/proc/*" \
  --exclude="/sys/*" \
  --exclude="/dev/*" \
  --exclude="/run/*" \
  --exclude="/tmp/*" \
  -e "ssh -i /root/.ssh/id_ed25519_backup -o BatchMode=yes" \
  /mnt/lvm-snap/ \
  backup@${BACKUP_HOST}:${BACKUP_BASE}/${TODAY}/

What these flags do: -aHAX preserves permissions, hardlinks, ACLs, and xattrs. --numeric-ids prevents UID/GID translation issues across servers. --delete keeps that day’s folder consistent with the snapshot.

Retention removes old dates separately.

If bandwidth is tight, cap throughput:

--bwlimit=25000

(That’s ~25 MB/s.)

Step 6: Clean up: unmount and remove the snapshot

After rsync completes:

sudo umount /mnt/lvm-snap
sudo lvremove -y /dev/ubuntu-vg/root_snap

Don’t leave snapshots around “just in case.” Long-lived snapshots can slow writes and increase fragmentation depending on workload.

Step 7: Add retention (daily + weekly) on the destination

Retention is easiest to manage on the backup server. This example keeps:

  • Last 14 daily backups
  • Last 8 weekly backups (Sunday copies)

Create a script on the destination: /usr/local/sbin/backup-retention-vps01.sh

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

BASE="/srv/backups/vps01"
cd "$BASE"

# Keep last 14 daily folders (YYYY-MM-DD)
ls -1d 20??-??-?? 2>/dev/null | sort | head -n -14 | xargs -r rm -rf

# Create/refresh weekly snapshots using hardlinks to save space (optional)
# Example: every Sunday, copy latest daily as weekly-YYYY-WW
LATEST="$(ls -1d 20??-??-?? 2>/dev/null | sort | tail -n 1)"
if [[ -n "${LATEST:-}" ]]; then
  WEEKLY="weekly-$(date -d "$LATEST" +%G-%V)"
  if [[ "$(date -d "$LATEST" +%u)" == "7" ]]; then
    rm -rf "$WEEKLY" 2>/dev/null || true
    cp -al "$LATEST" "$WEEKLY"
  fi
fi

# Keep last 8 weekly folders
ls -1d weekly-20??-?? 2>/dev/null | sort | head -n -8 | xargs -r rm -rf

Make it executable:

sudo chmod +x /usr/local/sbin/backup-retention-vps01.sh

Space note: cp -al uses hardlinks, so weekly copies don’t duplicate unchanged files. It works well with rsync-based dated folders, as long as the destination filesystem supports hardlinks (most do).

Step 8: Automate the whole job with a script + systemd timer

Put the source-side steps into a script. Run the same script manually and via a scheduler. That keeps debugging simple.

Create /usr/local/sbin/lvm-snapshot-backup.sh on the VPS:

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

LV="/dev/ubuntu-vg/ubuntu-lv"
SNAP_NAME="root_snap"
SNAP_SIZE="15G"
MNT="/mnt/lvm-snap"
BACKUP_HOST="backup.example.net"
BACKUP_BASE="/srv/backups/vps01"
KEY="/root/.ssh/id_ed25519_backup"
TODAY="$(date +%F)"

cleanup() {
  set +e
  mountpoint -q "$MNT" && umount "$MNT"
  lvdisplay "/dev/ubuntu-vg/${SNAP_NAME}" >/dev/null 2>&1 && lvremove -y "/dev/ubuntu-vg/${SNAP_NAME}" >/dev/null
}
trap cleanup EXIT

mkdir -p "$MNT"

lvcreate -L "$SNAP_SIZE" -s -n "$SNAP_NAME" "$LV" >/dev/null
mount -o ro /dev/ubuntu-vg/$SNAP_NAME "$MNT"

rsync -aHAX --numeric-ids --delete \
  --exclude="/mnt/*" \
  --exclude="/proc/*" \
  --exclude="/sys/*" \
  --exclude="/dev/*" \
  --exclude="/run/*" \
  --exclude="/tmp/*" \
  -e "ssh -i $KEY -o BatchMode=yes" \
  "$MNT/" \
  "backup@${BACKUP_HOST}:${BACKUP_BASE}/${TODAY}/"

Make it executable:

sudo chmod +x /usr/local/sbin/lvm-snapshot-backup.sh

Now create a systemd service: /etc/systemd/system/lvm-snapshot-backup.service

[Unit]
Description=LVM Snapshot Backup to Offsite (rsync)
Wants=network-online.target
After=network-online.target

[Service]
Type=oneshot
ExecStart=/usr/local/sbin/lvm-snapshot-backup.sh

And a timer: /etc/systemd/system/lvm-snapshot-backup.timer

[Unit]
Description=Run LVM Snapshot Backup nightly

[Timer]
OnCalendar=*-*-* 02:30:00
Persistent=true
RandomizedDelaySec=10m

[Install]
WantedBy=timers.target

Enable it:

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

Check logs after the first run:

journalctl -u lvm-snapshot-backup.service -n 200 --no-pager

Step 9: Do a restore verification (fast, boring, essential)

On the destination, pick the latest folder. Then confirm the backup includes what you actually need: web roots, configs, and service files.

cd /srv/backups/vps01
LATEST=$(ls -1d 20??-??-?? | sort | tail -n 1)

test -d "$LATEST/etc" && echo "OK: /etc present"
test -d "$LATEST/var/www" && echo "OK: /var/www present"

If you run Nginx, also check:

test -d "$LATEST/etc/nginx" && echo "OK: nginx config present"

For a deeper drill, schedule it quarterly. Restore to a new VPS, cut DNS over with a low TTL, and validate.

HostMyCode’s VPS restore drill tutorial shows a clean rehearsal that doesn’t risk production.

Troubleshooting: the failures you’ll actually see

  • Snapshot invalidated: the snapshot is too small. Increase SNAP_SIZE, shorten rsync time, or reduce write churn (logs, cache paths, noisy jobs).
  • Rsync permissions errors: confirm you ran rsync with sudo on the source and that the destination filesystem supports Unix permissions. Keep --numeric-ids.
  • SSH failures in timers: make sure BatchMode=yes works by running the same ssh command non-interactively. Verify the key path and confirm the destination won’t prompt for MFA.
  • Backups too big: exclude paths that don’t belong in backups (large caches, build artifacts). If disk usage grows quickly, see VPS cleanup steps for logs and cache.

Security checklist for offsite backups

  • Use a dedicated SSH key for backups, not your admin key.
  • Restrict the destination user to backup directories only; avoid shell access if you don’t need it.
  • Keep the backup server patched and firewall-limited to your source IP ranges.
  • Encrypt disks on the destination if your threat model includes physical compromise.
  • Test restores. It’s the only audit that matters.

If this VPS is still new, harden it before you rely on it. HostMyCode’s Ubuntu server hardening tutorial pairs well with this backup setup.

Summary: a backup flow you can trust

You now have snapshot-based backups that don’t depend on “quiet hours.” You also have an offsite copy that survives a total VPS failure.

What’s left is operational work: watch timer logs, track destination disk usage, and schedule a real restore drill.

If you want a VPS designed for this style of hands-on ops—SSH access, predictable storage, and room to grow—start with a HostMyCode VPS. If you’d rather offload patching, baseline hardening, and routine checks, managed VPS hosting keeps the same flexibility without making you the on-call team.

If you run production sites on a VPS, you need backups you can restore quickly and confidently. HostMyCode helps with predictable storage and reliable compute on a HostMyCode VPS, or a lighter ops burden with managed VPS hosting. Planning a move? HostMyCode migrations can handle the cutover with less risk.

FAQ

Does an LVM snapshot backup include my running database safely?

It’s filesystem-consistent, not application-consistent. For low-write sites, it often restores fine. For busy stores or apps, add database dumps or run a proper restore drill to prove it.

Can I use this on a shared hosting account?

No. Shared hosting rarely gives you LVM access. Use control-panel backups or application-level backups instead.

How big should the LVM snapshot be?

Big enough to cover writes during the rsync window, with headroom. Start with 10–20 GB for small hosting servers, then adjust based on observed write rates and rsync duration.

What’s better: rsync folders per day or an incremental backup tool?

Dated rsync folders are straightforward and easy to restore from. Incremental tools are usually more space-efficient and often add encryption. If you want encryption and dedupe, compare this with HostMyCode’s restic incremental backup tutorial.

How often should I test restores?

At least quarterly for production. After major upgrades or migrations, test again.