Skip to content
xaweho

Knowledge base · intermediate

Garbage collection explained — what actually frees up storage in PBS

Garbage collection (GC) clears out orphaned chunks left behind by prune. How GC works, when it runs and why it's allowed to take longer.

intermediate ·

Prune marks snapshots as no longer needed. But: the actual data blocks (chunks) stay on disk for now. Only garbage collection (GC) clears out orphaned chunks and frees the storage. Without GC, storage usage stays high, no matter how much you prune.

Why not delete directly?

The trick with PBS is deduplication: identical 4 MB data blocks are stored only once, even if they appear in 50 different snapshots. If prune deleted a snapshot along with all its chunks, it would destroy chunks that other snapshots still need.

Hence the two-step process:

  1. Prune deletes the snapshot entry (manifest + index)
  2. Garbage collection checks every chunk: is this chunk still referenced by any snapshot? If not → gone.

How GC works

GC runs in two phases:

Phase 1 — mark

PBS walks through all existing snapshots and marks every referenced chunk file as “in use”. This happens via an mtime touch — PBS updates the modification timestamp of all relevant chunk files to “now”.

Phase 2 — sweep

PBS scans the chunk pool and deletes all chunks whose mtime is older than a configurable threshold (default: 24h 5min). Those are the chunks that were neither marked in phase 1 nor freshly written by a running backup.

The 24h threshold is a safety net: chunks that were just written by a running backup shouldn’t get deleted by accident.

Where GC is configured

Datastore → Prune & GC in the PBS GUI.

There you’ll find:

  • GC Schedule: when GC runs automatically. Recommendation: sun 04:00 — early Sunday, when nothing is going on.
  • Status: when the last run was, how many chunks GC deleted, how long it took.

Start manually: Datastore → Prune & GC → Run garbage collection. (The CLI route via proxmox-backup-manager only runs on the PBS host itself — you don’t have shell access there, use the GUI.)

How long does GC take?

Depends on datastore size and number of chunks:

  • 100 GB datastore: 5–15 minutes
  • 1 TB datastore: 30–90 minutes
  • 10 TB datastore: 4–10 hours
  • 50 TB datastore: 1–3 days

GC is disk-I/O-intensive (it reads every chunk file). Our datastores run on SAS HDDs in RAID 10 behind hardware RAID controllers — that spreads the read load across multiple disks.

Backups can keep running in parallel while GC runs — PBS handles that cleanly. You don’t need to plan maintenance windows.

What do I see after a successful GC?

In the task log of a completed GC run:

INFO: starting garbage collection on store <datastore>
INFO: marking used chunks
INFO: marked 145823 chunks
INFO: removed 12384 chunks (size 49.2 GiB)
INFO: pending removals: 0 chunks
INFO: garbage collection finished after 1850s

Important lines:

  • marked X chunks: that many chunks are in use
  • removed X chunks (size Y): that much was freed
  • pending removals: chunks not yet deleted (e.g. because their mtime is too young)

Best practices

  • GC once a week is enough for most datastores.
  • Trigger manually after large prune actions: if you just did a mass prune (e.g. deleted an old VM from the backups entirely), start GC manually — otherwise it takes a while for the storage to free up.
  • Don’t run GC in parallel with verify: two I/O-intensive jobs at once make the datastore slow. Spread them out in the schedule (say, GC on Sunday, verify on Saturday).
  • Set up notifications: you want to know when GC fails — see email notifications.

Why sometimes less storage is freed than expected

Example: you pruned 100 GB of snapshots, GC runs, but only 30 GB come free. Why?

Deduplication. The pruned snapshots shared chunks with other snapshots that are still active. Those chunks stay — even though the snapshot whose manifest referenced them is gone. As long as another snapshot needs them, the chunks stay.

That’s not a bug, that’s by design. PBS stores data very space-efficiently — and the effect is that pruning doesn’t return as much storage as you’d naively expect.

More in Understanding storage usage.

Frequently asked questions

What if GC fails? Check the task log — usually it’s an I/O error or a corrupt chunk file. We monitor our datastores with regular verify jobs, RAID controller and SMART monitoring; if there’s a failure on our side, we contact you.

Can I stop GC manually? Yes, click “Stop” in the task window. GC shuts down cleanly — no data corruption, no half-finished state. You simply restart it later.

Why does GC take longer than the backup itself? GC reads every chunk file on disk — including the ones that aren’t referenced. Backup only writes the new chunks. That’s why GC is always slower than backup.

Are backups at risk while GC runs? No. Phase 1 (mark) marks all active chunks. If a backup runs during mark, those new chunks are freshly written too (with a current mtime) and not deleted in phase 2. PBS is concurrency-safe.

What does “pending removals” mean? Chunks that weren’t deleted in phase 2 because their mtime is too young (within the 24h threshold). They get checked again on the next GC run — if they’re still unreferenced, out they go.

Can I change the 24h threshold? On the CLI yes (--phase1-mode options), but we advise against it. The 24h window is exactly the safety buffer against race conditions between GC and running backups.

Why is prune+GC better than “normal” file deletion? Because PBS stores deduplicated. If it treated snapshots like normal files, you’d lose the benefit of deduplication. The way it works now, you can have 50 snapshots of 100 GB each that in reality only use 150 GB — and prune+GC cleans that up properly.

What’s next

Related products
Tags
pbs garbage-collection speicher

Did this article help?

If not, open a ticket. If it did, we're happy about a referral — both sides get €25 credit on their customer account.