Skip to main content
RunBook Academy

Proxmox VEXIII · Proxmox Backup ServerPBS architecture

Building the PBS host: install, storage layout, sizing

Advanced⏱ ~26 minproxmox-backup-managerzfsutils-linux

What you'll learn

  • State why a backup server on the hypervisor is not a backup, and what separation actually buys
  • Size RAM, datastore capacity and IOPS from a workload inventory and a retention policy
  • Lay out OS and datastore storage, including a ZFS special device for a spinning-disk datastore
  • Create a datastore from the CLI and set the tuning options that affect durability

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

Not yet marked complete on this device.

The rest of this part teaches PBS as something that already exists: you point a backup job at it, you prune it, you restore from it. This lesson is about building the thing. It comes late in the part because the design decisions only make sense once you know what a chunk store is — the hardware shape of a PBS host is a consequence of how the chunk store behaves, not a generic “backup server” spec.

Two of the decisions here cannot be changed later without rebuilding: the datastore filesystem, and whether the datastore has a metadata device. Both are made in the first hour and paid for over the following three years.

The separation rule

The Proxmox documentation is unambiguous, and it is worth quoting rather than paraphrasing:

Installing the backup server directly on the hypervisor is not recommended. It is safer to use a separate physical server to store backups.

The word doing the work there is physical. A PBS instance running as a VM on the cluster it protects fails the same way as a backup on the same disk as the data: the failure that destroys the source destroys the copy. The scenarios are not exotic:

FailurePBS on the clusterPBS on its own box
Ceph pool loses dataDatastore is on that pool. Backups gone.Unaffected
Cluster loses quorumPBS VM will not start; /etc/pve read-onlyUnaffected, still reachable
Bad kernel or firmware takes every node downNothing to restore fromRestore source intact
An operator with root on PVE is compromisedSame credentials reach the datastoreSeparate host, separate auth realm
You need to restore the clusterChicken and eggOrdinary restore

The last row is the one that turns an inconvenience into an outage of indefinite length. Every disaster-recovery plan that starts “restore the guests from PBS” assumes PBS is a thing that survives the disaster.

Sizing, derived rather than guessed

The published recommendations are the floor, not the answer:

ResourceDocumented recommendation
CPUModern 64-bit AMD or Intel, at least 4 cores
RAMMinimum 4 GiB for OS, filesystem cache and daemons, plus at least 1 GiB per TiB of storage
OS storage32 GiB or more, on hardware RAID with a battery-backed write cache, or a redundant ZFS setup
Backup storageFast local storage delivering high IOPS for random IO; enterprise SSDs for best results
NetworkRedundant multi-Gbit/s NICs

The RAM formula is the one people misread. It is additive: a 40 TiB datastore wants 4 + 40 = 44 GiB, not 4 GiB. It scales with the datastore because the workload that dominates PBS is not sequential streaming — it is metadata lookup across a very large number of small files, and that only works at speed if the metadata is cached.

The arithmetic that actually determines capacity

Start from the inventory, not from a percentage.

Read-only / Safecapacity worked example - run the arithmetic, do not eyeball it
set -euo pipefail

# --- inputs you must measure, not assume -----------------------------------
PROVISIONED_TIB=60      # sum of virtual disk sizes across all guests
USED_FRACTION=0.55      # how full those disks actually are (measure in-guest)
DAILY_CHANGE_PCT=2      # fraction of USED data rewritten per day
KEEP_DAILY=14
KEEP_WEEKLY=8
KEEP_MONTHLY=12
DEDUP_FACTOR=2.5        # placeholder ONLY until you have measured your own

# --- derived ----------------------------------------------------------------
USED_TIB=$(echo "$PROVISIONED_TIB * $USED_FRACTION" | bc -l)

# The first backup stores the used data once, after deduplication.
BASE_TIB=$(echo "$USED_TIB / $DEDUP_FACTOR" | bc -l)

# Every retained snapshot after the first adds only its unique chunks.
SNAPSHOTS=$(( KEEP_DAILY + KEEP_WEEKLY + KEEP_MONTHLY ))
DELTA_TIB=$(echo "$USED_TIB * $DAILY_CHANGE_PCT / 100 * $SNAPSHOTS" | bc -l)

RAW_TIB=$(echo "($BASE_TIB + $DELTA_TIB) * 1.30" | bc -l)   # 30% headroom

printf 'used            %.1f TiB\n' "$USED_TIB"
printf 'base after dedup %.1f TiB\n' "$BASE_TIB"
printf 'retained deltas  %.1f TiB over %d snapshots\n' "$DELTA_TIB" "$SNAPSHOTS"
printf 'buy at least     %.1f TiB usable\n' "$RAW_TIB"

Three things about that script matter more than the numbers it prints.

DEDUP_FACTOR is a placeholder and the script says so. Deduplication ratio is a property of your fleet, not of PBS. Fifty near-identical Debian VMs deduplicate spectacularly; fifty databases holding different customer data barely deduplicate at all. Plan with a conservative figure, then replace it with the real one after two weeks of backups — PBS reports it directly.

The 30% headroom is not padding. Garbage collection needs free space to work in, a datastore above roughly 80% starts allocating badly on any copy-on-write filesystem, and the single most common PBS incident is a datastore that filled up and stopped accepting the backups you were relying on.

Change rate dominates at long retention. At 2% daily change and 34 retained snapshots, the deltas are two thirds of a base copy. Doubling retention roughly doubles the delta term while leaving the base term untouched, which is why “keep everything for a year” is a capacity decision disguised as a policy decision.

Read-only / Safemeasure the real deduplication factor once you have data
# proxmox-backup-manager datastore list --output-format json | jq '.[] | {name, path}'
[
{
  "name": "store1",
  "path": "/mnt/datastore/store1"
}
]

Illustrative output

IOPS: why the chunk store wants SSD or a metadata device

A PBS datastore is not a big file. It is a directory of millions of small ones. From the technical overview:

The chunks of a datastore are found in <datastore-root>/.chunks/ … These chunk directories (0000-ffff) will be preallocated when a datastore is created.

That is 65,536 directories created up front, into which chunks are filed by the first two bytes of their checksum. For block-based backups the chunks are fixed size, typically 4 MiB. So:

Referenced dataChunks at 4 MiBChunks per directory
1 TiB262,144~4
10 TiB2,621,440~40
100 TiB26,214,400~400

Now consider garbage collection, which walks every index in the datastore and touches the access time of every chunk it references, then sweeps. On a 100 TiB datastore that is tens of millions of random metadata operations. On enterprise SSD it is a background job. On a spinning-disk pool with no metadata acceleration it is a job that runs for a day and competes with the backup window.

Laying out the disks

A workable three-tier layout for a dedicated PBS host:

TierDevicesPurpose
OS2 × small SSD, ZFS mirror (or hardware RAID1 with BBU)Debian 13 + PBS. 32 GiB minimum; 64+ GiB is more comfortable once task logs accumulate
Datastore dataHDD RAIDZ2 or SSD mirrors/RAID10The .chunks/ bulk
Datastore metadata2 × NVMe mirrored, as a ZFS special vdevOnly if the data tier is spinning
Destructivecreating the datastore pool - these commands erase the named devices
set -euo pipefail

# Confirm what PBS thinks the disks are before you touch them.
proxmox-backup-manager disk list

# Option A: let PBS build the pool and register the datastore in one step.
proxmox-backup-manager disk zpool create bulk \
--devices /dev/disk/by-id/wwn-0x5000c500aaaa0001,/dev/disk/by-id/wwn-0x5000c500aaaa0002,/dev/disk/by-id/wwn-0x5000c500aaaa0003,/dev/disk/by-id/wwn-0x5000c500aaaa0004 \
--raidlevel raidz2 \
--add-datastore true

# Option B: build the pool by hand when you need a special vdev, which the
# PBS helper does not create for you.
zpool create -o ashift=12 bulk \
raidz2 /dev/disk/by-id/wwn-0x5000c500aaaa0001 \
       /dev/disk/by-id/wwn-0x5000c500aaaa0002 \
       /dev/disk/by-id/wwn-0x5000c500aaaa0003 \
       /dev/disk/by-id/wwn-0x5000c500aaaa0004 \
special mirror /dev/disk/by-id/nvme-eui.0001 /dev/disk/by-id/nvme-eui.0002

zfs set compression=zstd atime=on relatime=off bulk
zfs create bulk/store1

Creating the datastore, and the two tuning options

Configuration changedatastore creation and schedules
set -euo pipefail

proxmox-backup-manager datastore create store1 /bulk/store1

# Schedules use systemd calendar-event syntax.
proxmox-backup-manager datastore update store1 \
--gc-schedule 'sat 03:00' \
--prune-schedule 'daily 01:00' \
--keep-daily 14 --keep-weekly 8 --keep-monthly 12

proxmox-backup-manager datastore list

Two tuning options exist and both are durability decisions rather than performance tweaks:

OptionValuesWhat it changes
chunk-orderinode (default), noneWhether verification and GC read chunks in filesystem-inode order or in index order. inode is dramatically better on spinning disks; on NVMe the difference is small
sync-levelnone, filesystem (default), fileHow aggressively PBS forces data to stable storage. file fsyncs each chunk; filesystem syncs at the end of a backup; none leaves it to the OS
Configuration changedatastore tuning
# Spinning-disk datastore, power-protected: the default ordering, maximum
# throughput.
proxmox-backup-manager datastore update store1 --tuning 'chunk-order=inode'

# A datastore on hardware with no power protection, where a host crash must
# not be able to leave a backup that verifies but cannot be read.
proxmox-backup-manager datastore update store1 --tuning 'sync-level=file'

The S3 backend, and what it does not remove

PBS 4 can place a datastore on S3-compatible object storage. The datastore is created against a configured endpoint and a local cache path:

Configuration changean S3-backed datastore still needs local disk
# The endpoint is configured first, under Configuration > Remotes > S3
# Endpoints in the GUI, or via the API.
proxmox-backup-manager datastore create archive /var/cache/pbs-archive \
--backend type=s3,client=s3-endpoint-1,bucket=backups-example

This is genuinely useful for a long-retention tier. It is not a way to avoid buying disks: the local cache is required, object-store latency makes it a poor primary tier for restore-time-sensitive workloads, and egress pricing turns a full-cluster restore into a line item somebody has to approve during an outage.

Knowledge check

Knowledge check · 4 questions

  1. Q1. A 40 TiB PBS datastore is planned. How much RAM does the published recommendation imply?

  2. Q2. Which statements about a ZFS special vdev in front of a spinning-disk PBS datastore are correct? Select all that apply.

  3. Q3. Mounting the datastore filesystem with noatime is a safe optimisation because it removes a write on every chunk read.

  4. Q4. The capacity script treats DEDUP_FACTOR as a placeholder rather than a constant. Why?

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