Skip to main content
RunBook Academy

LinuxXLVIII · Backup ToolsLVM snapshots

LVM snapshots for backups - the classic Linux approach

Intermediate⏱ ~10 minlvm2

What you'll learn

  • Create LVM snapshots
  • Use snapshots for backup
  • Quiesce a database with the PostgreSQL 15+ backup API, or a plain filesystem with fsfreeze
  • Recognise the production impact
  • Remove snapshots cleanly

Prerequisites

Verified against Ubuntu 24.04 LTS · Debian 12 (Bookworm) · RHEL 9.x · Rocky Linux 9.x · AlmaLinux 9.x · Linux kernel 6.1 LTS / 6.6 LTS · systemd 255+ · OpenSSH 8.7p1 (RHEL 9) / 9.6p1 (Ubuntu 24.04) · nftables 1.0.x · chrony 4.x · Pacemaker 2.1.x · Corosync 3.1.x · 2026-08-09

Not yet marked complete on this device.

LVM snapshots are a classic Linux tool for backup. They work with any filesystem on LVM and are instant (copy-on- write). The watch-out: snapshot overflow degrades performance.

Create an LVM snapshot

# Create a snapshot of an LV
lvcreate -L 1G -s -n mydata-snap /dev/vg0/mydata

# Verify
lvs

The -s flag creates a snapshot. -L 1G allocates 1 GB of COW (copy-on-write) space. The snapshot is instant.

Use the snapshot for backup

# Mount the snapshot
mkdir /mnt/snap
# XFS refuses to mount a filesystem whose UUID is already mounted, and a
# snapshot carries the origin's UUID - so on XFS this needs `nouuid`.
# ext4 has no such restriction.
mount -o ro,nouuid /dev/vg0/mydata-snap /mnt/snap   # XFS
# mount -o ro       /dev/vg0/mydata-snap /mnt/snap   # ext4

# Backup from the snapshot - -aAXH, not -a: ACLs, xattrs and SELinux
# labels are not part of -a, and a restore without them fails at start-up
rsync -aAXH --numeric-ids /mnt/snap/ /backup/mydata/

# Or tar - note -C and the trailing '.'
tar --acls --xattrs --numeric-owner --sparse \
    -czf /backup/mydata-$(date +%F).tar.gz -C /mnt/snap .

# Unmount
umount /mnt/snap

The snapshot is read-only mounted. The original LV is unaffected; backup proceeds from the snapshot.

Remove the snapshot

# Remove the snapshot
lvremove /dev/vg0/mydata-snap

Always remove snapshots promptly. An old snapshot accumulates COW data and degrades performance.

Production impact

LVM snapshots are copy-on-write:

  • Every write to the original LV allocates a new block in the COW space.
  • A snapshot left in place for a long time accumulates COW blocks.
  • When COW space is full, the snapshot is “invalid” and cannot be used.

For a busy LV with many writes, a 1 GB snapshot can fill in minutes. Plan accordingly — which means three concrete things, not a resolution to be careful.

Size the COW space from the write rate, not from habit. What the snapshot must hold is everything written to the origin while it exists, so the input is bytes-written-per-hour multiplied by how long the backup takes. 15–20% of the origin is a reasonable starting point for a nightly backup window; a 1G default on a 100 GB busy volume is not a small snapshot, it is a snapshot that will be invalid before the backup finishes.

Let LVM grow it. In /etc/lvm/lvm.conf:

snapshot_autoextend_threshold = 70
snapshot_autoextend_percent   = 20

At 70% full the snapshot grows by 20%, repeatedly, while the VG has free extents. This does nothing unless the monitoring daemon is running — it is dmeventd, started by lvm2-monitor:

systemctl is-active lvm2-monitor      # must print: active
sudo lvchange --monitor y /dev/vg0/mydata-snap

The default snapshot_autoextend_threshold is 100, which means autoextend is off. Setting the percent without lowering the threshold changes nothing.

Check the fill level, and check it before you trust the backup. lvs reports it in Data%:

# Watch it during the backup
lvs -o lv_name,origin,lv_size,data_percent,lv_attr vg0

# Gate the backup on the snapshot still being valid afterwards.
# lv_attr character 5 is the State field: 'a' active, 'I' Invalid snapshot,
# 'S' invalid Suspended snapshot (man 8 lvs). Character 1 is the volume
# TYPE - 's' for snapshot - so do not test that one.
ATTR=$(sudo lvs --noheadings -o lv_attr /dev/vg0/mydata-snap | tr -d '[:space:]')
case "${ATTR:4:1}" in
    I|S) echo 'snapshot invalidated - COW space exhausted, backup is NOT usable' >&2
         exit 1 ;;
esac

That last check is the one most backup scripts omit. An invalidated snapshot does not make tar or rsync fail; the reads simply stop returning the data you expect, and the job exits 0 with an archive nobody can restore from.

Application-consistent snapshots

The PostgreSQL backup API is session-scoped. Starting the backup in one psql -c and stopping it in another does not work: the first client disconnects, the backup is aborted, and the snapshot in between carries no backup_label. Run the whole bracket in one session and let psql fire the lvcreate from inside it:

set -euo pipefail

psql -v ON_ERROR_STOP=1 <<'SQL'
SELECT pg_backup_start('lvm-snap', fast => true);
\! lvcreate -L 20G -s -n pgdata-snap /dev/vg0/pgdata
SELECT * FROM pg_backup_stop();
SQL

mount -o ro,nouuid /dev/vg0/pgdata-snap /mnt/snap   # drop nouuid on ext4
# Backup, then write the label returned by pg_backup_stop()
# into the copy as backup_label
umount /mnt/snap
lvremove -y /dev/vg0/pgdata-snap

set -euo pipefail and -v ON_ERROR_STOP=1 are the load-bearing part of that script, not boilerplate. Without them a failed pg_backup_start() still lets lvcreate run, and you end up holding a crash-consistent snapshot that your runbook calls application-consistent.

Simpler, and the option to reach for first: pg_basebackup -D /backup/base -Ft -z -Xs -P -c fast does the bracketing and ships the WAL for you, with no snapshot involved.

Size the COW space for the whole backup window, not just the snapshot instant - the snapshot must survive until the copy finishes.

Quiescing a volume with no backup API

Not every volume holds a database. For a plain filesystem the generic quiesce is fsfreeze: it flushes dirty pages, blocks new writes, and leaves the on-disk state consistent for the instant the snapshot is taken.

# Freeze, snapshot, thaw. The freeze window is milliseconds.
sudo fsfreeze -f /data
sudo lvcreate -L 20G -s -n data-snap /dev/vg0/data
sudo fsfreeze -u /data

Write it so the thaw cannot be skipped:

set -euo pipefail
trap 'sudo fsfreeze -u /data || true' EXIT
sudo fsfreeze -f /data
sudo lvcreate -L 20G -s -n data-snap /dev/vg0/data

When to use

LVM snapshots are best for:

  • Hosts with LVM.
  • Filesystems on LVM logical volumes.
  • Quick, scriptable snapshots.
  • Database hosts, when the quiesce is held in one session.

Not best for:

  • Non-LVM filesystems.
  • Long-term retention (use file backup for that).
  • Critical performance hosts (snapshot has overhead).

Knowledge check

Knowledge check · 5 questions

  1. Q1. What happens when an LVM snapshot fills its COW space?

  2. Q2. LVM snapshots are persistent across reboots.

  3. Q3. Which of the following are valid LVM snapshot commands? Select all that apply.

  4. Q4. A nightly script calls psql -c "SELECT pg_start_backup(...)", then lvcreate, then pg_stop_backup. The cluster was upgraded to PostgreSQL 16 a year ago. The job has reported success every night since. What do you actually have?

  5. Q5. A script runs fsfreeze -f /data, the lvcreate that follows fails, and the script exits. What does the operator see?

Passing score: 75%. Answers are checked in this browser.