← Back to Wiki
Proxmox / Backups
Set Up Proxmox Backup Server
Proxmox Backup Server (PBS) gives your Proxmox VE fleet real deduplicated, incremental backups
— but the NFS storage setup has a permissions gotcha that trips up almost everyone the first time.
The setup
PBS runs happily as its own VM. Point it at NFS-backed storage from a NAS for the actual backup datastore:
mkdir -p /mnt/pbs-backups
mount -t nfs <nas-ip>:/volume1/pbs-backups /mnt/pbs-backups
echo "<nas-ip>:/volume1/pbs-backups /mnt/pbs-backups nfs defaults,_netdev,vers=4.1 0 0" >> /etc/fstab
Gotcha: PBS refuses to initialize a datastore in a non-empty directory, and many NAS
vendors automatically create a hidden recycle-bin folder at the share root. Point the datastore at a clean
subdirectory instead of the share root:
mkdir -p /mnt/pbs-backups/datastore
The NFS permission gotcha (the one that gets everyone)
PBS needs to write to the datastore as root to build its internal structure. Most NAS NFS
export rules default to "root squash" — mapping the remote root user down to an unprivileged account, which
silently blocks exactly this. Set the NFS permission rule for PBS's IP explicitly to
Read/Write,
squash: No mapping. Even with that rule set correctly, the first backup run can still fail with a
permission error if the actual directory permissions on the share are still restrictive — if so, fix them
directly on the NAS itself, not through the NFS client:
ssh <nas>
sudo chmod -R 777 /volume1/pbs-backups
Connecting PBS to your hypervisor
This needs a token/ACL setup on both sides:
- On PBS: create an API token (
Configuration → Access Control → API Tokens)
and grant it the datastore admin role:
proxmox-backup-manager acl update /datastore/<name> DatastoreAdmin --auth-id 'root@pam!<token-name>'
- On your hypervisor: add PBS as a storage target, using the
user@realm!tokenname format for the username field and the token secret (not the
token name) as the password.
If storage registration fails with 401 Unauthorized, check the username field includes the
!tokenname suffix. If it then fails with "cannot find datastore," check
proxmox-backup-manager acl list on PBS — a token can authenticate successfully while still
having zero actual permissions on anything, which produces exactly this error.
Backup job configuration
A reasonable starting retention policy: keep-last 3, keep-daily 7, keep-weekly 4, keep-monthly 2,
keep-yearly 1. Note that many backup job configurations use an explicit VM/container ID list, not
"all" — meaning every new VM or container needs to be added to the job by hand. This is an easy
thing to forget when spinning up new infrastructure; worth a periodic audit to catch anything that slipped
through.
Sizing your NAS storage realistically
Don't trust df on a shared NFS mount to estimate actual backup datastore size.
df reports usage for the entire underlying volume, which may be shared by many unrelated
exports on the same NAS. Use du -sh on the actual exported path instead to get the real number
— the difference can be enormous (one real case: df showed 23TB, the actual datastore was
185GB).
Migrating the datastore to new storage later
If you ever need to move the backing storage to a different NAS, the safe sequence is: mount the new share
alongside the old one, rsync the datastore across, verify file counts and sizes match on both
sides, then cut over. Cutover itself needs the PBS services stopped first — the daemon holds a lock file
open on the datastore, so unmounting fails with "device busy" while it's running. Update the mount config to
point at the new location, remount at the same path, and restart the services. If the datastore
name and mount path don't change, no hypervisor-side configuration needs updating at all — it only talks to
PBS's API, never the NAS directly.
Gotcha: if a migration script combines a potentially-failing command (like an unmount) with
a config-file edit in one block under set -e, a failure in the first command silently skips the
edit entirely — the script just exits. Don't rely on strict-mode ordering when chaining a command that might
fail with one that absolutely must run regardless; check exit codes explicitly instead.
Don't forget to actually retire the old copy
It's tempting to leave the old storage location as a permanent safety net after a migration, but "temporary
rollback copy" has a way of quietly becoming permanent clutter. Once the new storage has a genuine track
record — several days of successful backups and verification/garbage-collection jobs, not just "the
cutover didn't immediately explode" — retire the old copy deliberately rather than leaving it indefinitely:
- Confirm nothing still references the old storage (check any scheduled job configs, not just your memory
of what you set up) before removing it.
- Delete the actual old backup data, not just the pointer/config entry to it — removing a storage
definition from your hypervisor doesn't free any space on the NAS behind it.
Gotcha: a "dedicated direct link" network (a 10G point-to-point connection between your hypervisor
and NAS, for example) is often genuinely unreachable from anywhere except the two ends of that link
— trying to reach it from a third machine (your workstation, say) can time out in a way that looks like a
transient network hiccup but is actually working exactly as designed. If a host has two addresses (a fast
dedicated link and a normal LAN address), use the normal LAN address for anything that isn't the hypervisor
itself.
Backing up a physical Windows PC (not just VMs)
PBS isn't limited to backing up VMs and containers — a real, physical Windows machine can back up to the
same datastore. The catch: Proxmox doesn't actually ship an official Windows client. The
real option is a third-party, alpha-quality community reimplementation of the backup protocol, distributed
as two small executables — one for folder-level backups, one for whole-disk image backups. Both are
unsigned, so expect Windows Defender to flag or slow them down, and treat "alpha quality" as a literal
warning, not just a disclaimer.
Gotcha: a PowerShell script launched by double-clicking closes itself the instant the process exits
or crashes — taking any error output with it. A folder-level backup run crashed silently partway
through and looked like a normal, boring exit; only checking the actual datastore afterward (and finding a
corrupted, incomplete snapshot) revealed anything had gone wrong. Two fixes: always launch from an
already-open terminal rather than double-clicking, and have the script pause for input at the very end
(success or failure) so a double-click launch can't hide a crash behind a closing window.
For anything where "don't lose a single file" actually matters more than "back up efficiently," a
full-disk image (capturing the whole drive via a live snapshot) is the safer default over picking specific
folders — it doesn't depend on remembering every folder that matters, and it survives a folder-level tool's
rougher edges.
The permission bug that looks exactly like a config error
An API token's real permissions can be the *intersection* of two separate grants, not just its
own. Some backup/virtualization platforms let you grant a role directly to a token and
separately to its parent user account — and the token's actual effective access is whichever permissions
both of them agree on. Granting a role only to the token, while its parent user has none, produces a token
that authenticates fine but can do nothing — every request gets rejected, and a simple "list this token's
granted roles" check looks completely correct the whole time, because it only shows what was granted, not
what's actually effective. If a freshly-permissioned token still gets rejected and the ACL looks right,
check whether the platform has this two-sided model before assuming the credential itself is wrong — grant
the same role to both the token and its parent account.
Scaling this to a whole household
Once more than one physical machine needs this, a few small pieces of infrastructure make it repeatable
instead of a manual one-off each time:
- A small dedicated internal file server (a lightweight Linux box running just a file-sharing service) to
host the backup agent files, so a new machine can grab everything it needs from one internal address
instead of chasing down a GitHub release each time.
- A script that mints a separate, independently-revocable credential per machine under
one shared backup account — rather than reusing a single shared token everywhere. Losing or retiring one
machine then means revoking exactly one credential, with zero effect on any other device's backups. The
same script also double-checks the new credential actually resolves real permissions (see the gotcha
above) before ever handing it off, rather than discovering the problem on the target machine later.
- Deliberately scoped network access — a home network with separate VLANs (a kids/guest network, an IoT
network, etc.) doesn't need blanket access between all of them for this to work. Opening just the
specific path each network segment actually needs (this one file server, this one port) keeps the
isolation that VLANs exist for in the first place, rather than punching a hole wide enough to defeat the
whole point of segmenting the network.