13

Backups & PBS

Proxmox has two layers of backup. vzdump decides how to read a consistent image of a guest without stopping it for long. Proxmox Backup Server decides how to store it: split into chunks, named by their SHA-256, stored once no matter how many backups refer to them. Together they turn nightly full backups into “read what changed, send what's new”.

vzdump modes: what the guest feels

--mode stopguest: running
downtime: shutdown + boot
shutdown
boot
QEMU backup job (copy-before-write)

Clean shutdown gives a fully consistent image. The backup starts while the VM is off, then the VM boots again and runs while the job keeps reading.

--mode suspendguest: running
downtime: short pause
suspended
QEMU backup job (copy-before-write)

Kept for compatibility: suspends the VM, then does a snapshot-mode backup. Longer pause than snapshot mode, no real consistency gain. Use snapshot.

--mode snapshotguest: running
downtime: ≈ none
QEMU backup job (copy-before-write)

Live backup. With the guest agent (agent: 1), filesystems are frozen for a moment (guest-fsfreeze-freeze/thaw) so the image is file-system consistent.

guest running paused / frozen down booting backup running
copy-before-write

How snapshot mode works with no storage snapshot

For VMs, Proxmox doesn't need ZFS or LVM snapshots: the backup runs inside QEMU itself. When the job starts, the backup must contain the disk exactly as it was at that instant. QEMU reads the disk front to back; if the guest tries to overwrite a block the job hasn't read yet, QEMU first copies the old block to the backup and only then lets the write through. The guest pays with write latency, which hurts when the target is slow or far away. Fleecing parks those old blocks on fast local storage instead, decoupling guest I/O from backup speed.

Snapshot mode without a snapshot: copy-before-write

live disk (what the guest sees) · click a block to write it
backup on PBS (point-in-time = job start)

Press ▶ to start a snapshot-mode backup, then click blocks to make the guest write.

guest writes
0
copy-before-write
0
write stall
0 ms
proxmox backup server

Chunks: the unit of everything in PBS

A VM disk is cut into fixed 4 MiB chunks. Each chunk is hashed with SHA-256, optionally compressed (zstd) and encrypted, and stored as a file named by its digest. A backup snapshot is just an index: an ordered list of digests. Two backups that share a chunk share one file. The client skips uploading chunks the previous snapshot of the same guest already has, and for running VMs QEMU's dirty bitmap tells it which chunks were even written since the last backup, so it doesn't have to read the rest. Change some chunks, back up, prune, run GC:

PBS: fixed chunks, dirty bitmap, dedup, GC

VM disk, 4 MiB chunks · click to let the guest change onedirty bitmap: none yet
snapshots on the datastore (each is just an index: a list of chunk digests)
no backups yet
chunk store (.chunks/ — one file per unique digest)

The VM has never been backed up. Press “backup” for the first, full run.

logical
0 MiB
on disk
0 MiB
dedup
–
Dirty bitmap = incremental reads
QEMU keeps a bitmap of changed blocks in memory per disk. It survives live migration, but not a VM stop/start, and is dropped if the last backup on the server is gone or the encryption key changed. Then the next run reads everything again but still uploads only new chunks.
GC: mark and sweep with atime
Pruning only deletes index files. Garbage collection then (1) walks every index and touches every chunk it references (updating its atime), (2) deletes chunks whose atime is older than the cutoff: 24 h 5 min before GC started, or the start of the oldest running backup if that is earlier. That grace window protects chunks a running backup just reused but hasn't indexed yet.
Encryption happens on the client
With a key, chunks are encrypted with AES-256-GCM before leaving the node; PBS stores ciphertext and never sees the key. Keep a copy of the key (paperkey, or a master key) off the cluster: without it the backups are noise. Dedup then only works between backups using the same key.
chunking

Fixed chunks for disks, dynamic chunks for files

Why file archives use dynamic chunks

fixed 12-byte chunks (like VM images)11 reused · 0 new
#!/bin/sh⏎export PATH=/usr/bin⏎log() { echo "$1" >> /var/log/app.log; }⏎log starting⏎exec /usr/bin/app --config /etc/app.conf
content-defined chunks (like .pxar)12 reused · 0 new
#!/bin/sh⏎export PATH=/usr/bin⏎log() { echo "$1" >> /var/log/app.log; }⏎log starting⏎exec /usr/bin/app --config /etc/app.conf

Insert one line at the top. Fixed boundaries all shift, so every later chunk looks new and gets uploaded again. With content-defined boundaries, cut points depend on the bytes around them, not on their offset, so after the edit the boundaries fall back into the same places and the rest of the stream deduplicates. A block device doesn't shift its data around, so VM images use fixed 4 MiB chunks; a file archive does, so .pxar uses dynamic ones.

prune

Retention: which backups survive

Prune options are evaluated in order: last, hourly, daily, weekly, monthly, yearly. Each keeps the newest backup of each of its N most recent periods, skipping periods a previous option already covered. So keep-weekly=4 means “four more weeks beyond those already kept”, not “the last four weeks”. Change the numbers and hover cells.

Prune simulator

Oct
Nov
Dec
Jan
Feb
Mar
Apr
May
Jun
Jul
Aug
Sep
Mon
Wed
Fri
Sun
kept by last (3) kept by daily (7) kept by weekly (4) kept by monthly (6) kept by yearly (1) pruned no backup
prune-backups: keep-last=3,keep-daily=7,keep-weekly=4,keep-monthly=6,keep-yearly=1 → keeps 21 of 325
verify · sync · offsite

Trusting the backups

Verify jobs
Re-read every chunk of selected snapshots and check it against its digest. Corrupt chunks are renamed to .bad and the snapshot is flagged, so the next backup re-uploads the data. Schedule it: bit rot is silent until restore day.
Sync jobs & remotes
Copy snapshots to (push) or from (pull) another PBS. Only missing chunks travel, so an offsite copy over a WAN is cheap. With encrypted backups, the offsite PBS holds ciphertext only.
Replication is not backup
pvesr sends incremental ZFS snapshots of local disks to another node every few minutes so HA or migration can start there with little data loss. It mirrors mistakes too: a deleted file or ransomware arrives on the replica at the next sync. Use it next to backups, not instead.
vzdump → local / NFSvzdump → PBSpvesr replication
unit.vma.zst / .tar.zst file per runchunks + index per snapshotZFS snapshot on another node
incrementalno, full every timeyes (dirty bitmap + dedup)yes (zfs send -i)
historyper retentionper retention, cheaponly latest (+ few snaps)
survives a bad deleteyesyesno, replicated next sync
restore speedfull readfull read, or live-restore / single fileinstant: start from replica
cli

Commands and files

on Proxmox VE
# attach a PBS datastore as storage (client-side encryption key generated on the PVE side)
pvesm add pbs pbs1 --server 10.0.0.5 --datastore store1 \
    --username backup@pbs --password '...' \
    --fingerprint 'ab:cd:...:ef' --encryption-key autogen

# one-off backup, live, with fleecing on fast local storage
vzdump 100 --mode snapshot --storage pbs1 \
    --fleecing enabled=1,storage=local-lvm --notes-template '{{guestname}}'

# local file backup instead (one .vma.zst / .tar.zst per run, no dedup)
vzdump 200 --mode snapshot --storage local --compress zstd

pvesm list pbs1 --vmid 100     # what's there
qmrestore pbs1:backup/vm/100/2026-10-03T21:00:02Z 100 --storage ceph-vm
scheduled job
# /etc/pve/jobs.cfg  (Datacenter → Backup)
vzdump: backup-nightly
	schedule 21:00
	all 1
	mode snapshot
	storage pbs1
	fleecing enabled=1,storage=local-lvm
	notes-template {{guestname}}
	prune-backups keep-last=3,keep-daily=7,keep-weekly=4,keep-monthly=6
	enabled 1
what a PBS datastore looks like
/mnt/datastore/store1/
├── .chunks/                 # 65536 prefix dirs: 0000 … ffff
│   ├── 3fa1/3fa1c9…e2       # one file per unique chunk, named by SHA-256
│   └── …
├── vm/100/2026-10-03T21:00:02Z/
│   ├── index.json.blob      # manifest (signed if encrypted)
│   ├── qemu-server.conf.blob
│   └── drive-scsi0.img.fidx # fixed index: list of chunk digests
└── ct/200/2026-10-03T21:05:40Z/
    └── root.pxar.didx       # dynamic index: digests + offsets
proxmox-backup-client / proxmox-backup-manager
# file-level backup of any Linux host (dynamic chunks, .pxar)
export PBS_REPOSITORY='backup@pbs@10.0.0.5:store1'
proxmox-backup-client backup root.pxar:/ etc.pxar:/etc --keyfile ~/.pbs.key
proxmox-backup-client snapshot list
proxmox-backup-client prune host/web01 --keep-daily 7 --keep-weekly 4 --dry-run

# on the PBS server
proxmox-backup-manager garbage-collection start store1
proxmox-backup-manager verify store1
proxmox-backup-manager remote create offsite --host pbs2.example.com \
    --auth-id sync@pbs --password '...' --fingerprint '...'
proxmox-backup-manager sync-job create offsite-pull --store store1 \
    --remote offsite --remote-store store1 --schedule 'daily'
  • why it mattersUse snapshot mode for VMs: QEMU's copy-before-write gives a point-in-time image with no downtime. Add the guest agent for filesystem consistency and fleecing when the backup target is slow.
  • why it mattersPBS stores chunks, not files. Every backup is logically full but physically incremental, so you can keep long histories cheaply and restore any point without a chain.
  • why it mattersPrune deletes indexes; GC frees space later. Verify and sync jobs are what make a backup trustworthy: an unverified, single-copy backup is a hope, not a plan.