Proxmox VEXIII · Proxmox Backup ServerPBS integration
Backing up non-PVE hosts with proxmox-backup-client
What you'll learn
- Back up an arbitrary Linux host into a PBS datastore from the command line
- Handle credentials and encryption passphrases without writing secrets into a script
- Design exclusions and archive splits that make a host restorable rather than merely copied
- Schedule, monitor and prune host backups without a PVE cluster in the picture
Prerequisites
Verified against Proxmox VE 9.2.4 · Proxmox Backup Server 4.2.5 · Ceph Squid / Tentacle · Debian 13 (Trixie) · Linux kernel 7.0 (PVE 9.2 default) · 2026-08-12
Every estate has hosts that are not virtual machines on your cluster: the physical database server nobody will virtualise, the cloud instance running the public-facing thing, the build machine, the NAS, the router with a configuration you would hate to rebuild. They usually end up on a different backup product, with a different retention policy, a different restore procedure and a different set of people who know how to use it.
proxmox-backup-client puts them in the same datastore as everything else.
Same deduplication, same verification, same prune and garbage-collection
machinery, same restore skills. It is a standalone binary that talks to PBS
directly; PVE is not involved at any point.
Installing the client
The client ships in the Proxmox repositories, and on a non-Proxmox Debian or Ubuntu host you add the client repository rather than the full PBS one.
set -euo pipefail
apt update && apt install -y ca-certificates wget
wget -O /usr/share/keyrings/proxmox-archive-keyring.gpg \
https://enterprise.proxmox.com/debian/proxmox-archive-keyring-trixie.gpg
cat > /etc/apt/sources.list.d/pbs-client.sources <<'EOF'
Types: deb
URIs: http://download.proxmox.com/debian/pbs-client
Suites: trixie
Components: main
Signed-By: /usr/share/keyrings/proxmox-archive-keyring.gpg
EOF
apt update && apt install -y proxmox-backup-client
proxmox-backup-client versionThe repository string and the credential
Everything the client does is aimed at a repository, whose format is:
[[username@]server[:port]:]datastore
The username defaults to root@pam if unset — which, per the previous lesson,
is exactly what you do not want a standalone host holding.
set -euo pipefail
proxmox-backup-manager user create db01@pbs --comment 'Physical DB server db01'
proxmox-backup-manager user generate-token db01@pbs client
# DatastoreBackup: can write and restore its own backups; cannot prune.
for id in 'db01@pbs' 'db01@pbs!client'; do
proxmox-backup-manager acl update /datastore/store1/hosts/db01 \
DatastoreBackup --auth-id "$id"
doneset -euo pipefail
install -d -m 0700 /etc/proxmox-backup
umask 077
cat > /etc/proxmox-backup/client.env <<'EOF'
PBS_REPOSITORY=db01@pbs!client@pbs1.example.com:store1
PBS_NAMESPACE=hosts/db01
PBS_PASSWORD_FILE=/etc/proxmox-backup/token.secret
PBS_ENCRYPTION_PASSWORD_FILE=/etc/proxmox-backup/key.passphrase
PBS_FINGERPRINT=aa:bb:cc:dd:ee:ff:00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:00:11:22:33:44:55:66:77:88:99
EOF
printf '%s\n' 'REPLACE_ME_WITH_THE_TOKEN_SECRET' > /etc/proxmox-backup/token.secret
chmod 0600 /etc/proxmox-backup/token.secret /etc/proxmox-backup/client.envPBS_FINGERPRINT verifies the server certificate. Setting it means a client
that finds itself talking to a different server — because DNS was poisoned,
or because someone stood up a lookalike — refuses rather than uploading your
data to it.
Encryption, and the key you must not lose
Client-side encryption is the feature that makes it reasonable to back up a sensitive host into a shared datastore, or into a datastore you do not administer.
set -euo pipefail
proxmox-backup-client key create /etc/proxmox-backup/db01.key
chmod 0600 /etc/proxmox-backup/db01.key
# A printable copy for the safe. This is the escrow that matters.
proxmox-backup-client key paperkey /etc/proxmox-backup/db01.key \
> /root/db01-paperkey.txtThe backup itself
set -euo pipefail
set -a; . /etc/proxmox-backup/client.env; set +a
proxmox-backup-client backup \
root.pxar:/ \
--keyfile /etc/proxmox-backup/db01.key \
--exclude /proc --exclude /sys --exclude /dev --exclude /run \
--exclude /tmp --exclude /var/tmp \
--exclude /var/lib/postgresql \
--exclude /mnt --exclude /media
proxmox-backup-client snapshot listThree design decisions are embedded in that command and they are the difference between a copy and a restorable host.
Split archives by restore unit, not by directory tidiness. An archive is the granularity at which you restore. Anything you might want to recover without touching the rest deserves its own archive:
set -euo pipefail
set -a; . /etc/proxmox-backup/client.env; set +a
proxmox-backup-client backup \
system.pxar:/ \
data.pxar:/srv/data \
dbdump.pxar:/var/backups/pgdump \
--keyfile /etc/proxmox-backup/db01.key \
--exclude /proc --exclude /sys --exclude /dev --exclude /run \
--exclude /srv/data --exclude /var/backups/pgdumpExclusions belong in two places. Pseudo-filesystems and caches go on the
command line, where they are visible in the unit file and in review. Anything
application-specific is better expressed as a .pxarexclude file in the
directory it concerns, where it lives next to the thing it describes and
survives a rewrite of the backup script. Patterns are one per line, #
introduces a comment, ! reverses a pattern into an inclusion, a leading /
anchors to the current directory and a pattern without one matches in
subdirectories too.
A live database directory is not a backup of the database. The exclusion
of /var/lib/postgresql above is deliberate: what is included is
/var/backups/pgdump, produced by a dump that runs before the backup. Copying
running database files with a file-level tool produces exactly the crash
consistency problem from xiii-pbs-backup-modes, without even the benefit of
a coordinated snapshot.
Scheduling it
There is no backup job scheduler outside PVE, and that is fine, because systemd already is one.
set -euo pipefail
cat > /etc/systemd/system/pbs-backup.service <<'EOF'
[Unit]
Description=Back up this host to Proxmox Backup Server
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
EnvironmentFile=/etc/proxmox-backup/client.env
ExecStartPre=/usr/local/sbin/pre-backup-dump.sh
ExecStart=/usr/bin/proxmox-backup-client backup \
system.pxar:/ data.pxar:/srv/data dbdump.pxar:/var/backups/pgdump \
--keyfile /etc/proxmox-backup/db01.key \
--exclude /proc --exclude /sys --exclude /dev --exclude /run \
--exclude /srv/data --exclude /var/backups/pgdump
Nice=10
IOSchedulingClass=idle
EOF
cat > /etc/systemd/system/pbs-backup.timer <<'EOF'
[Unit]
Description=Nightly PBS backup
[Timer]
OnCalendar=*-*-* 01:30:00
RandomizedDelaySec=1800
Persistent=true
[Install]
WantedBy=timers.target
EOF
systemctl daemon-reload
systemctl enable --now pbs-backup.timer
systemctl list-timers pbs-backup.timerRandomizedDelaySec matters more than it looks: forty hosts with an
OnCalendar of 01:30 all connect at 01:30 and turn a manageable load into a
thundering herd against one datastore. Persistent=true runs a missed backup
after the host comes back, which is the behaviour you want on a machine that
is not always on.
set -euo pipefail
STORE=store1
MAX_AGE_HOURS=36
# snapshot list prints one line per snapshot; the group is the first column.
# Keep only the newest line per group and compare its timestamp to now.
proxmox-backup-client snapshot list --repository "$STORE" \
| awk 'NR>2 {print $2}' \
| sort -t/ -k1,2 -k3,3r \
| awk -F/ '!seen[$1"/"$2]++ {print $0}' \
| while read -r snap; do
when=$(printf '%s' "$snap" | sed 's|.*/||')
age=$(( ( $(date +%s) - $(date -d "$when" +%s) ) / 3600 ))
if [ "$age" -gt "$MAX_AGE_HOURS" ]; then
printf 'STALE %-40s %s h old\n' "$snap" "$age"
fi
doneRestoring a host
set -euo pipefail
set -a; . /etc/proxmox-backup/client.env; set +a
proxmox-backup-client snapshot list
# 1. Browse, decide, extract selectively.
proxmox-backup-client catalog shell 'host/db01/2026-08-11T01:30:00Z' data.pxar
# 2. Mount read-only and copy out what you need.
mkdir -p /mnt/restore
proxmox-backup-client mount 'host/db01/2026-08-11T01:30:00Z' data.pxar /mnt/restore
# ... copy ...
umount /mnt/restore
# 3. Restore a whole archive into a staging directory.
proxmox-backup-client restore 'host/db01/2026-08-11T01:30:00Z' \
data.pxar /srv/staging/db01-restore/Knowledge check
Knowledge check · 4 questions
Q1. A scheduled host backup needs the token secret. Which mechanism is the most defensible for a production host with a secret manager available?
Q2. Because PBS stores the chunks, a client-side encryption key that is lost can be recovered by the PBS administrator.
Q3. Which of these are sound reasons to split a host backup into several named archives rather than one? Select all that apply.
Q4. Forty hosts each run a systemd timer with OnCalendar set to 01:30. What single directive most improves this?
Passing score: 75%. Answers are checked in this browser.