Manual · Chapter 7

Restore

Go to the contents

7.1 From a local store#

bkp_restore --store <dir>/<store>.cbs (--name <backup> [--at <time>] | --run <run>) --target <file-or-device>
            [--threads <n>] [--assume-zeroed] [--no-progress] [--decrypt-key file:<path>] [--index-dir <dir>]
            [--strict | --damage-file <path>] [--background]
  • --store is the store directory, for example /backup/default.cbs. Its generations are read along; if a generation <store>.g<date>.cbs is named, only that one.
  • --name takes the newest run of this backup name, with --at the newest up to this point in time. --run chooses exactly one run (name from --list or UUID).
  • --target is the destination file or device. It is overwritten. A file is truncated and written sparse; a device must be at least as large as the source.
  • --decrypt-key file:<path> is required when the store is encrypted; a missing or wrong key stops the restore before the destination is opened.
  • --assume-zeroed tells the restore that the destination already contains only zeros (for example a freshly created thin volume). Holes are then not written.
  • Every chunk is checked against its ID while it is read. --verify is accepted only for compatibility.

Showing instead of restoring

bkp_restore --store /backup/default.cbs --list [--name data]       # all runs, oldest first
bkp_restore --store /backup/default.cbs --name data --show-run     # the chosen run as JSON, nothing is written

Important: The restore overwrites the destination without asking. Check the path of --target twice, especially with block devices (/dev/...). Use --assume-zeroed only if the destination really contains only zeros; a freshly created, normal LVM volume is not guaranteed to be zeroed.

7.2 Straight from the server#

Instead of --store, bkp_restore reads the store straight from a bkp_target. First --list shows what is there:

bkp_restore --server backup.example.org:9990 --server-dir vm1 \
            --tls-ca /etc/bkp/target.crt --api-key file:/etc/bkp/vm1.key --list

Output:

RUN                                                     CREATED              NAME   TYPE           SOURCE      DATA      SENT       NEW  STATUS
20261015T121825Z-6aa5d6f2-8419-482a-ab3f-0373e33b449d   2026-10-15 14:18:25  data   Full         16.0 MiB  13.0 MiB  12.0 MiB  12.0 MiB  ok
20261015T121831Z-8c47f436-cdc7-4638-a3f8-d347e0c5ba7c   2026-10-15 14:18:31  data   Incremental  16.0 MiB  13.0 MiB   2.2 MiB   2.2 MiB  ok

The list is sorted by age, the oldest first; --name filters, --store <name> chooses a store other than default. This is how to restore the newest backup of a name:

bkp_restore --server backup.example.org:9990 --server-dir vm1 \
            --tls-ca /etc/bkp/target.crt --api-key file:/etc/bkp/vm1.key \
            --name data --target /dev/vg0/data_restored

bkp_restore reads the manifest from the server and fetches the chunks in batches; nothing is cached on disk. If the connection breaks, the restore continues after reconnecting (--net-retry, default 30 minutes).

7.3 At a certain point in time (point in time)#

Every run is a possible restore point, and every one is restored equally fast: no chain is merged. The state of early yesterday is chosen with --at, a certain run with --run:

bkp_restore --server ... --name data --at "2026-10-14 06:00" --target /dev/vg0/data_pit
bkp_restore --server ... --run 20261015T121825Z-6aa5d6f2-8419-482a-ab3f-0373e33b449d --target /dev/vg0/data_pit

--at takes YYYY-MM-DD[ HH:MM[:SS]] in local time or, with an appended Z, in UTC (2026-10-14T06:00Z), and chooses the newest run up to this point in time. Locally from a store this works the same way.

7.4 From the sinks of a profile#

Without --store and --server, bkp_restore reads the profile --name and its sinks from the agent configuration (/etc/bkp/bkp_agent.cfg with sinks.cfg, others with --config-path). Locations, keys and API keys come from the [sink:] sections:

bkp_restore --name srv01 --target /dev/vg0/root_restored              # from the first sink that has the backup
bkp_restore --name srv01 --sink rz1 --target /srv/restore/root.img    # explicitly from rz1
bkp_restore --name srv01 --list                                       # column SINKS: where every backup is

Restored is the newest backup of the name across all sinks, read from the first sink in sinks= order that has it (local copy first = fast). What this sink cannot deliver (chunk missing or damaged, server gone during the restore) comes from the twin run of another sink; every such place appears as a warning in the log.

7.5 From an archive#

An archive written with bkp_maintenance export (Chapter 10) needs no store:

bkp_restore --archive /mnt/usb/data-20261015T121849Z-44fdc352-....cba --target restored.img
dd if=/dev/nst0 bs=1M | bkp_restore --archive - --target /dev/vg0/data_restored     # from tape, front to back

Output:

[bkp_restore] INFO  restoring run 20261015T121849Z-44fdc352-... ('data' of 2026-10-15 14:18:49, 16777216 bytes)
                    from the archive /mnt/usb/data-20261015T121849Z-44fdc352-....cba to restored.img
[bkp_restore] INFO  done: 16777216 bytes restored (data 13631488, holes 3145728), 44 chunks, 12583143 stored bytes read

7.6 Damaged data: partial restore#

A chunk that no store can deliver (missing, damaged, not present in any twin either) does not stop the restore: its bytes are written as zeros, and it goes on. An unreadable segment turns its whole range into zeros. At the end the damaged ranges are logged, and the exit code is 3.

  • --damage-file <path> also writes the damaged ranges as JSON (offset and length), for example to find the files affected.
  • --strict stops at the first damage instead (exit 1).
  • An unreadable run, a wrong key, a lost connection without a twin or a write error always make the restore fail (exit 1).
Exit codeMeaning
0restored
1error
2usage error (options)
3partial: damaged ranges are zeros

7.7 Virtual machines#

bkp_proxmox and bkp_xenV back up a whole VM as one VM run (all disks plus configuration). bkp_restore --disk <slot> restores a single disk from it (for example scsi0, xvda); --allow-incomplete also allows a VM run that is missing other disks. Whole VMs are restored by bkp_proxmox_restore and bkp_xenV_restore, also straight from the server, with --at and from an archive (see README). For Hyper-V VMs there are bkp_hyperV and bkp_hyperV_restore (Chapter 20).

bkp_proxmox backs up disks on LVM, LVM-thin, ZFS, Ceph RBD and as a qcow2 file (directory, NFS, CIFS), plus small raw files such as the vTPM state. Depending on the storage, the snapshot is an LVM snapshot, a ZFS snapshot (read through a clone), an RBD snapshot (read through rbd device map) or an internal qcow2 snapshot (read through qemu-nbd); a raw file up to 64 MiB is copied while the guest is frozen. Large raw or vmdk files are refused, with the next step in the message. bkp_proxmox_restore turns a qcow2 disk back into a qcow2 file where the destination storage knows qcow2, otherwise into a raw volume. So that the guest is frozen only briefly, bkp_proxmox talks to the guest agent and QEMU directly and takes the qcow2 snapshots of all disks in one step and the RBD snapshots at the same time; the duration is in the log.

With --ram-state (profile: ram-state=yes) bkp_proxmox also backs up the memory of a running VM, like a Proxmox snapshot with “Include RAM”: Proxmox takes the snapshot itself (qm snapshot … --vmstate 1), bkp_proxmox backs up the disks and the RAM image as one VM run and removes the snapshot again afterwards. For that, all disks must be on LVM-thin with room there for about the RAM of the VM; this is checked beforehand. The guest stands still while Proxmox writes the memory (roughly used RAM divided by the write rate of the storage); the duration is in the log. A stopped VM is backed up normally, with a note.

bkp_proxmox_restore puts a VM backed up this way down in a paused state: qm start <vmid> continues it at the time of the backup, with all programs running. MAC addresses and SMBIOS UUID are kept. If the original VM exists in the cluster, the network cards are disconnected (link_down=1); the message names the command to connect them. The clock in the guest is behind until NTP corrects it. With --no-ram-state the VM boots instead, with a new MAC address and SMBIOS UUID as with any other restore.

7.7.1 Fast follow-up backups (CBT)#

For a running VM, QEMU keeps track for bkp_proxmox of which areas of each disk the guest has written since the last backup (dirty bitmaps, one per disk and job, named aegisgrid-…). The next backup reads only these areas; the store takes the rest over from the previous backup without reading it. That saves time and load on the storage, above all for large disks with few changes. It applies to every kind of storage (LVM, LVM-thin, ZFS, Ceph RBD, qcow2) and is on without any setting.

The store takes data over per segment (1 GiB): a segment the guest has written to is read in full, so a disk smaller than 1 GiB is read in full on every change. How much changed and how many segments were taken over is in the log.

A disk is read in full, with a plain note in the log (not an error):

  • on the first backup of the job; the follow-up backups then read only the changes
  • when the VM is stopped: there is no QEMU to keep track
  • after the VM was restarted (stopped and started, not a reboot inside the guest), migrated or rolled back to a Proxmox snapshot: the bitmaps live only as long as the QEMU process
  • after the disk was enlarged
  • with --type full and when --no-fs-bitmap differs from the previous backup
  • when QEMU does not answer or something about the bitmap is not right: whenever in doubt, in full

If a backup is cancelled, even hard (kill -9, the backup process losing power), the next run of the job or bkp_proxmox --discard-interrupted takes the change tracking of the cancelled run back; no change is lost. A held run (bkp_sched hold) continues with its data. Moving the disk (qm disk move) does not interrupt the change tracking. Bitmaps of other programs, such as pbs-bitmap-… of Proxmox Backup Server, are left alone.

Option / profileMeaning
--cbt auto / cbt=autodefault: after the first backup, read only the changes
--cbt off, --no-cbt / cbt=offevery backup reads in full; what earlier backups of the job set up in QEMU is removed

bkp_proxmox --check says whether the next backup reads only the changes. Xen domains do not have fast follow-up backups yet; Hyper-V uses RCT for them (Chapter 20).

In the management console and the web interface, the tick “Fast follow-up backups (CBT)” in the VM profile switches the feature off and on again (unticked: cbt=off); it appears in Proxmox profiles once the scheduler supports it. For a successful run, the list of runs shows how the disks were read, for example “Successful, only the changes read (1.2 GiB)” or “Successful, read in full”. The tooltip of the list of runs, the status line of the log window and the run details of the web interface give the reason for a full read per disk; the mail of the run has the same in the line “Follow-up:”.

bkp_xenV --ram-state (profile: ram-state=yes) backs up a running Xen domain with its memory in the same way: xl save -p writes its state as an image and leaves it paused; at that moment the snapshots of all disks are taken, after which it carries on running. During the backup the image is kept in a thin LV in the pool of the first disk, otherwise in a normal LV in its volume group, otherwise in a file in /var/lib/bkp_xenv/ramstate; the choice is in the log. All disks must be LVM volumes; hooks and --file-freeze do not apply. The domain stands still while the memory is written; the duration is in the log.

bkp_xenV_restore writes the image next to the new configuration; the log names xl restore <cfg> <image> to continue and xl create <cfg> for a fresh start. With --resume the domain carries on at once. The MAC addresses are kept; if a domain with the same MAC is running, the restore warns, and with --resume the network cards stay disconnected from the bridge until the original is off. --no-ram-state starts the domain without its memory.

7.8 Restoring a VM in place#

If the VM still exists but its content is no longer usable (an operating error, a failed update, ransomware), --in-place gives it back the disks of an earlier state, and it keeps its VMID or name:

bkp_proxmox_restore --name vm101 --store /backup/default.cbs --at "2026-11-10 06:00" --in-place --stop --start
bkp_xenV_restore --name web01 --store /backup/default.cbs --in-place --dry-run       # check first
bkp_xenV_restore --name web01 --store /backup/default.cbs --in-place --changed-only  # write back only what changed
  • Switching over safely (default): each disk is written anew first, beside the present one. Only when all are done is the VM switched over; the previous disks are kept (Proxmox: as unused disks, Xen: as …-alt-<time>), and so is the previous configuration. If the restore fails or is stopped, the VM stays as it was.
  • The backed-up configuration replaces the present one, with the backed-up MAC addresses; --keep-config keeps the present one and only swaps the disks.
  • Refused are a running VM (--stop shuts it down first), a VM that is locked or managed by HA, a VM on another node and a VM that is not the one backed up (different name and UUID; --force-other-vm). The message names the next step.
  • --remove-old deletes the previous disks once the restore succeeded (not while a Proxmox snapshot needs them).
  • Without room for a second copy, --overwrite writes straight into the present disks, and --changed-only writes only the blocks that differ from the state – much faster after a small change. Neither has a way back: a restore that stops halfway leaves the disks partly overwritten.

bkp_restore --changed-only does the same for a single disk or volume: each block is compared with the target, and only what differs is fetched from the backup and written. In the management console this is “Write only the changed blocks” when restoring to the original place or to another disk.

7.9 Emergency: host set up anew#

If a Proxmox or Xen host is lost and installed anew, its VM backups are still on the backup server. This brings the VMs back, with their old VMID or name:

  1. Install the package on the new host (Chapter 2), with the scheduler.
  2. In the management console, open the client and under “Scheduler” › “Destinations (sinks)...” create the sink to the backup server with “New...”: the same backup server, the same directory and the same API key as before; for an encrypted sink enter your own with the key you kept (do not generate a new one: another key cannot read the backups).
  3. “Scheduler” › “Restore...” shows all backups on the client's destinations, also without a job. Choose the VM, the point in time and “as a new VM”.
  4. The wizard suggests the VMID the VM had at the backup when it is free in the cluster; with Xen the domain keeps its name. The VM keeps its MAC addresses as long as no other VM uses them, so a guest with a fixed DHCP reservation gets its usual address. The VM is created switched off: check the network, then start it.
  5. Then set up the backup jobs again (profile and job) so the VMs are backed up further on.

The same on the command line, straight from the backup server:

bkp_proxmox_restore --server backup.example.org:9990 --server-dir pve1 --tls-ca target.crt \
    --api-key file:/etc/bkp/pve1.key --name vm101 --vmid original -- --decrypt-key file:/root/pve1-sink.key
bkp_xenV_restore --server backup.example.org:9990 --server-dir xen1 --tls-ca target.crt \
    --api-key file:/etc/bkp/xen1.key --name web01 -- --decrypt-key file:/root/xen1-sink.key

--vmid original takes the VMID from the backup and refuses when it is in use (then --in-place replaces the disks of the existing VM); --prefer-original-vmid takes it only when it is free, otherwise the next free one. bkp_xenV_restore keeps the domain's name as long as the host has no domain of that name. In both cases the MAC addresses are kept when no other VM or running domain uses them (otherwise new ones; --keep-network-identity always keeps them).

7.10 Checking the restore#

cmp data.img restored.img && echo IDENTICAL            # for image files
fsck -n /dev/vg0/data_restored                          # check the file system (read-only)
mount -o ro /dev/vg0/data_restored /mnt && ls /mnt      # look at the contents

Note: Because free file system areas come back as zeros (Chapter 3), a raw comparison cmp with the original device only matches for backups with --no-fs-bitmap. A clean fsck and a comparison of the files (diff -r, rsync -n) is the right test. For LVM sources bkp_verifier automates this comparison against a snapshot kept with --keep-snapshot.