Back to tutorials
Tutorial

Snapshot Backup Tutorial (2026): Fast VPS & Dedicated Server Rollbacks with LVM, Btrfs, and Offsite Sync

Snapshot backup tutorial for VPS and dedicated servers in 2026: LVM/Btrfs snapshots, safe quiescing, and offsite sync.

By Anurag Singh
Updated on Sep 14, 2026
Category: Tutorial
Share article
Snapshot Backup Tutorial (2026): Fast VPS & Dedicated Server Rollbacks with LVM, Btrfs, and Offsite Sync

A good backup gets you back online. A good snapshot gets you back online fast. This snapshot backup tutorial shows how to create filesystem snapshots (LVM or Btrfs) in seconds.

Next, you’ll sync that snapshot offsite. That way, you can recover even after a full disk loss. Your services stay up, and you still get real disaster recovery instead of “hope the VPS survives.”

This guide targets VPS and dedicated server admins on Ubuntu 24.04/26.04 LTS, Debian 12/13, AlmaLinux 9/10, and Rocky Linux 9/10. Examples assume root access and a typical hosting stack: Nginx/Apache, PHP-FPM, mail, and WordPress.

What snapshots are (and what they are not)

Snapshots are point-in-time views of a filesystem or volume. With LVM and Btrfs, creation is almost instant and starts small. As live data changes, the snapshot preserves old blocks (copy-on-write). That means it grows over time.

  • Snapshots are great for: fast rollback after a bad update, capturing a stable view for an offsite copy, short-retention safety nets.
  • Snapshots are not enough alone: if the disk is corrupted, stolen, or encrypted by malware, snapshots on that same disk can disappear with it.

The production workflow is simple: snapshot locallycopy snapshot offsiteprove you can restore.

If rollback planning is part of a larger move, keep DNS cutover clean too. This DNS cutover checklist complements the backup side nicely.

Before you start: pick the right approach for your server

Snapshots work best when they match your storage setup. Don’t force a method that doesn’t fit your current disk layout.

Option A: LVM snapshots (common on VPS images)

  • Works well on ext4/xfs inside an LVM logical volume.
  • Easy to mount the snapshot read-only and copy it.
  • Snapshot space must be planned; if it fills, the snapshot becomes invalid.

Option B: Btrfs snapshots (clean and flexible if you already run Btrfs)

  • Subvolume snapshots are fast and simple.
  • Send/receive can be efficient for incremental replication.
  • Best if your root or /home is already Btrfs; migrating to Btrfs is a separate project.

Option C: Hypervisor/provider snapshots (fast, but treat carefully)

  • Often one-click, fast rollback.
  • Consistency depends on whether the guest filesystem is quiesced.
  • Still needs offsite copies if you care about disaster recovery.

If you want a server where you can schedule snapshots and keep enough disk headroom for them, start with a VPS plan sized for your write rate and growth. HostMyCode’s HostMyCode VPS is a good fit for hands-on admins.

If you’d rather offload patching, monitoring, and day-to-day upkeep, use managed VPS hosting. Treat snapshots as an extra safety net.

Step 1: inventory your disks and confirm you can snapshot

Confirm what’s mounted where. Then confirm you have enough free space for a snapshot and an offsite copy.

# Show block devices and filesystems
lsblk -f

# Check disk usage (watch for low free space)
df -hT

Check for LVM

pvs
vgs
lvs -a -o +devices

If you see volume groups (VG) and logical volumes (LV), LVM snapshots are on the table.

Check for Btrfs

findmnt -no FSTYPE /

# If it returns btrfs:
btrfs subvolume list /

Step 2: make the snapshot consistent (quiesce the right services)

On hosting servers, the hard part isn’t static files. It’s the always-changing data. That includes databases, mail spools, and anything writing in tight loops.

Your goal is simple: don’t copy half-written data.

Minimal downtime method (recommended): database dump + filesystem snapshot

Freezing every write on a live server is painful. It’s also easy to get wrong. A practical approach is to dump databases into a dedicated directory. Then snapshot the filesystem including those dumps.

Most sites won’t notice.

# Create a backup staging directory
install -d -m 700 /root/backup-staging

MySQL/MariaDB (common for WordPress):

# Dumps all databases with routines/events; adjust credentials as needed
mysqldump --all-databases --single-transaction --routines --events --triggers \
  --quick --lock-tables=false \
  > /root/backup-staging/mysql-all.sql

gzip -9 /root/backup-staging/mysql-all.sql

PostgreSQL (if you have it):

sudo -u postgres pg_dumpall > /root/backup-staging/pg-all.sql
gzip -9 /root/backup-staging/pg-all.sql

Mail servers: if you host mail locally, plan a short maintenance window when you need strict consistency. Under heavy mail load, you can pause delivery briefly:

# Postfix: hold deliveries briefly (optional)
postsuper -h ALL

After the snapshot is taken, release the queue:

postsuper -H ALL

If mail is part of the service you deliver, keep rDNS and SPF/DKIM/DMARC in shape. That reduces retries and backlog growth. This walkthrough covers the checklist: VPS email setup tutorial.

Step 3A (LVM): create a snapshot safely and mount it read-only

With LVM, you snapshot a logical volume. The snapshot needs space to store changed blocks.

On busy web servers, 10–20% of the LV size is a reasonable starting point. Then adjust based on how fast snapshots grow in your environment.

Example layout

Assume your root filesystem is /dev/vg0/root.

# Confirm the LV path
lvs

# Create a snapshot (name it with a timestamp)
SNAP=rootsnap-$(date +%F-%H%M)
lvcreate -L 10G -s -n "$SNAP" /dev/vg0/root

Mount it read-only at a temporary mount point:

install -d /mnt/lvm-snap
mount -o ro,norecovery /dev/vg0/"$SNAP" /mnt/lvm-snap

Quick diagnostic: watch snapshot usage

Check snapshot usage before you start a long transfer. If it fills, the snapshot can invalidate mid-copy.

lvs -o lv_name,lv_size,data_percent,metadata_percent,lv_attr /dev/vg0

Pitfall: On write-heavy servers (WooCommerce orders, big mail queues, chatty logs), a small snapshot can fill quickly. Reduce churn during the backup window. Rotate logs, pause heavy jobs, or allocate a larger snapshot.

Step 3B (Btrfs): snapshot a subvolume and prepare it for export

Btrfs snapshots are easiest when your data is split into subvolumes. Common layouts use @ for root and @home for home. If your distro image already uses that layout, you’re in good shape.

# See subvolumes
btrfs subvolume list /

Create a read-only snapshot (a good default for backup copies):

install -d /btrfs-snapshots

# Example: snapshot the root subvolume @ (adjust to your layout)
SNAP=@-$(date +%F-%H%M)
btrfs subvolume snapshot -r /@ /btrfs-snapshots/"$SNAP"

Pitfall: If your sites live on a separate filesystem (for example ext4 at /home), snapshotting only / won’t include them. Use findmnt to confirm where your real data sits. Then snapshot each relevant mount.

Step 4: copy the snapshot offsite (rsync over SSH)

Offsite storage should be boring. Use an SSH-only backup user on another VPS, a dedicated backup box, or a storage server. What matters is failure separation.

Your backup target must not share the same disk, node, or vendor blast radius as production.

If you don’t already have a destination, a small “backup receiver” VPS is a common pattern. HostMyCode’s VPS plans work well for this setup: one production node, one backup node.

Create a restricted backup user on the destination

On the backup server:

adduser --disabled-password --gecos "" backup
install -d -m 700 -o backup -g backup /srv/backups

On the production server, generate a dedicated SSH key:

ssh-keygen -t ed25519 -a 64 -f /root/.ssh/backup_ed25519 -C "snapshot-backup"

Copy the public key to the destination:

ssh-copy-id -i /root/.ssh/backup_ed25519.pub backup@BACKUP_SERVER_IP

If SSH isn’t locked down yet, do that before you automate anything. This guide keeps it practical: SSH lockdown tutorial.

Rsync the snapshot content

LVM snapshot mount example:

RSYNC_SSH="ssh -i /root/.ssh/backup_ed25519"
SRC="/mnt/lvm-snap/"
DEST="backup@BACKUP_SERVER_IP:/srv/backups/$(hostname -s)/$SNAP/"

rsync -aHAX --numeric-ids --delete --info=stats2 \
  -e "$RSYNC_SSH" \
  --exclude='/proc/*' --exclude='/sys/*' --exclude='/dev/*' \
  --exclude='/run/*' --exclude='/tmp/*' \
  "$SRC" "$DEST"

Btrfs snapshot directory example (if you mounted it in the main filesystem):

RSYNC_SSH="ssh -i /root/.ssh/backup_ed25519"
SRC="/btrfs-snapshots/$SNAP/"
DEST="backup@BACKUP_SERVER_IP:/srv/backups/$(hostname -s)/$SNAP/"

rsync -aHAX --numeric-ids --delete --info=stats2 -e "$RSYNC_SSH" "$SRC" "$DEST"

Quick diagnostic: confirm you actually copied what you think you copied

# On the production server
rsync -n -aHAX --delete -e "$RSYNC_SSH" "$SRC" "$DEST" | head

# On the backup server
du -sh /srv/backups/$(hostname -s)/$SNAP

Tip: Big, fast-growing logs are a common reason snapshots and transfers fail. Get rotation under control first. This hosting-safe walkthrough helps: Logrotate tutorial.

Step 5: unmount and remove the snapshot (don’t keep them forever)

Snapshots aren’t archives. Keep local snapshots short-lived (hours to a day) unless you’ve budgeted disk and tested growth. Use offsite copies for longer retention.

LVM cleanup

umount /mnt/lvm-snap
lvremove -y /dev/vg0/"$SNAP"

Btrfs cleanup

btrfs subvolume delete /btrfs-snapshots/"$SNAP"

Step 6: automate with a systemd timer (safer than cron for visibility)

Cron works. Systemd timers usually give better visibility. You get structured logs, exit codes, and a predictable runtime environment.

On plain VPS builds, timers are often easier to operate. If you standardize on cron across a fleet, keep cron. Just don’t skip monitoring.

Create the backup script

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

cat > /usr/local/sbin/snapshot-backup.sh <<'EOF'
#!/usr/bin/env bash
set -euo pipefail

BACKUP_HOST="BACKUP_SERVER_IP"
BACKUP_USER="backup"
KEY="/root/.ssh/backup_ed25519"
RSYNC_SSH="ssh -i $KEY"

# Adjust for your LVM LV
LV="/dev/vg0/root"
MNT="/mnt/lvm-snap"

install -d -m 700 /root/backup-staging

# Database dump (adjust if you use socket auth)
mysqldump --all-databases --single-transaction --routines --events --triggers \
  --quick --lock-tables=false \
  > /root/backup-staging/mysql-all.sql

gzip -f -9 /root/backup-staging/mysql-all.sql

SNAP="rootsnap-$(date +%F-%H%M)"

# Create and mount snapshot
lvcreate -L 10G -s -n "$SNAP" "$LV"
install -d "$MNT"
mount -o ro,norecovery "/dev/vg0/$SNAP" "$MNT"

DEST="$BACKUP_USER@$BACKUP_HOST:/srv/backups/$(hostname -s)/$SNAP/"

rsync -aHAX --numeric-ids --delete --info=stats2 \
  -e "$RSYNC_SSH" \
  --exclude='/proc/*' --exclude='/sys/*' --exclude='/dev/*' \
  --exclude='/run/*' --exclude='/tmp/*' \
  "$MNT/" "$DEST"

# Cleanup
umount "$MNT"
lvremove -y "/dev/vg0/$SNAP"
EOF

chmod 700 /usr/local/sbin/snapshot-backup.sh

Why this script is intentionally conservative: it dumps databases first, then snapshots, then copies. The snapshot won’t be a perfect “single moment” image of every write on the box. Restores are still predictable for common hosting workloads.

Create the systemd service

Create /etc/systemd/system/snapshot-backup.service:

cat > /etc/systemd/system/snapshot-backup.service <<'EOF'
[Unit]
Description=Snapshot backup to offsite host (LVM + rsync)
Wants=network-online.target
After=network-online.target

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

Create the timer

Create /etc/systemd/system/snapshot-backup.timer:

cat > /etc/systemd/system/snapshot-backup.timer <<'EOF'
[Unit]
Description=Nightly snapshot backup timer

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

[Install]
WantedBy=timers.target
EOF

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

View logs after the first run

systemctl start snapshot-backup.service
journalctl -u snapshot-backup.service -n 200 --no-pager

Step 7: restore test (the part most people skip)

A restore test turns “we take backups” into “we can recover.” You don’t need a full bare-metal restore to validate the pipeline. Pick one site and one database, and test monthly.

File restore test (single directory)

On the backup server, locate your copied snapshot data. Then pull one directory back to a staging path on production:

# From production server
RESTORE_SRC="backup@BACKUP_SERVER_IP:/srv/backups/$(hostname -s)/$SNAP/home/exampleuser/public_html/"
RESTORE_DST="/root/restore-test-public_html/"

rsync -aHAX -e "ssh -i /root/.ssh/backup_ed25519" "$RESTORE_SRC" "$RESTORE_DST"

ls -la "$RESTORE_DST" | head

Database restore test (to a temporary DB)

Example for MariaDB/MySQL: copy the dump back, then import into a scratch database. This catches truncated dumps and broken permissions early, when it’s cheap to fix.

# Create scratch DB
mysql -e "CREATE DATABASE restore_test;"

# Import (adjust path to your gz dump if needed)
zcat /root/backup-staging/mysql-all.sql.gz | mysql restore_test

# Sanity check
mysql -e "SHOW TABLES;" restore_test | head

If you run cPanel/WHM, account-level restores are often smoother through WHM’s backup tools. This guide focuses on safe testing: cPanel backup restore tutorial.

Hosting-specific checklist: keep snapshot backups from breaking production

  • Plan snapshot headroom: keep at least 20–30% free on the volume hosting snapshots, or your snapshot can fill during large updates.
  • Exclude churn: cache directories, tmp, and runtime mounts don’t belong in offsite backups.
  • Rotate logs: uncontrolled logs are snapshot killers. Fix rotation before you blame “slow backups.”
  • Check email queues: large mail queues mean high churn. If you host mail, verify rDNS and deliverability so retries don’t spiral.
  • Measure copy time: if offsite sync runs into peak hours, throttle with --bwlimit or reschedule.
  • Track failures: a timer that fails silently is not a backup system. Alert on non-zero exit codes.

Common troubleshooting

“LVM snapshot invalidated” or data_percent hits 100%

  • Increase snapshot size (example: 20G instead of 10G) or reduce change rate during transfer.
  • Shorten the time between snapshot creation and removal. Don’t create a snapshot and leave it while you debug something else.
  • Rotate logs and disable noisy debug logging before the backup window.

Rsync is slow or causes load spikes

  • Add --bwlimit=50M to cap bandwidth.
  • Prefer off-peak schedules and use Nice/IOScheduling in the systemd unit (already included above).
  • Verify you’re not copying giant directories that don’t matter (cache, node_modules, tmp builds).

You copied /proc and /sys and now the backup is weird

That’s exactly why the rsync examples exclude them. If you already copied them, delete the destination folder and recopy from a clean snapshot.

WordPress restores but shows wrong URLs

This is usually a migration/URL mismatch, not a backup problem. If your restore test uses a different hostname, update WordPress URLs and make sure SSL is valid.

For a stable LetsEncrypt setup on VPS, keep this nearby: VPS SSL setup guide.

Summary: a practical snapshot backup tutorial workflow you can trust

Use snapshots for speed, not long-term safety. Take the snapshot quickly (LVM or Btrfs), sync it offsite with rsync over SSH, then remove the local snapshot right away.

Automate runs with a systemd timer. Keep restore tests on your calendar. That combination covers both bad deployments and total server loss.

If you’re building this on new infrastructure, start with a reliable base. A HostMyCode VPS gives you root-level control for LVM/Btrfs snapshot workflows, while managed VPS hosting is a better fit if you want help keeping the server stable while you focus on sites and customers.

If you run client websites, recovery speed comes from two things: stable infrastructure and a backup routine you can repeat under stress. HostMyCode offers VPS hosting for hands-on admins and managed VPS hosting when you want patching and monitoring handled while you keep control of your stack.

FAQ

Can I rely on provider snapshots instead of LVM/Btrfs?

Use provider snapshots for quick rollback, but still keep an offsite copy. Provider snapshots usually live in the same vendor ecosystem. They won’t save you from account-level or region-level issues.

How often should I take snapshots?

For hosting servers, nightly is a sensible baseline. If you deploy frequently or handle transactions (WooCommerce), add a pre-deploy snapshot and keep it for 24 hours.

Do snapshots capture database consistency?

Not automatically. That’s why this tutorial dumps databases first, then snapshots the filesystem. For strict consistency with near-zero data loss, you need database-native replication or coordinated freezing.

What’s a safe retention policy?

Keep local snapshots short-lived (hours to one day). Keep offsite copies longer (for example, 7–30 days) depending on how quickly you need to detect and recover from corruption.

Will this work on cPanel servers?

Yes for the underlying Linux volumes, but cPanel account restores are often smoother through WHM backups. If you run WHM, test restores with WHM tools and use snapshots as a fast “oops button” for system-level changes.

Snapshot Backup Tutorial (2026): Fast VPS & Dedicated Server Rollbacks with LVM, Btrfs, and Offsite Sync | HostMyCode