Using PBS with QEMU dirty bitmaps

Pasiu

New Member
Nov 4, 2025
5
0
1
Hi all,

I’m trying to understand how Proxmox Backup Server (PBS) works with QEMU dirty bitmaps (.qcow2).

I created a virtual machine and run it like this:

Code:
qemu-system-x86_64 \
  -m 2048 \
  -smp 2 \
  -blockdev driver=qcow2,file.driver=file,file.filename=/root/vm/debian.qcow2,node-name=drive0 \
  -device virtio-blk-pci,drive=drive0 \
  -net nic \
  -net user,hostfwd=tcp::2222-:22 \
  -name test-vm \
  -nographic \
  -serial tcp:127.0.0.1:4444,server,nowait \
  -monitor none \
  -qmp unix:/tmp/qmp-sock,server,nowait

Then I use QMP (JSON) to create snapshots:
Code:
# enable QMP capabilities
{ "execute": "qmp_capabilities" }

# create a new dirty bitmap
{
  "execute": "block-dirty-bitmap-add",
  "arguments": { "node": "drive0", "name": "pbs", "persistent": true }
}

# if bitmap exists but is not recording, remove and re-add:
{ "execute": "block-dirty-bitmap-remove", "arguments": { "node": "drive0", "name": "pbs" } }
{ "execute": "block-dirty-bitmap-add", "arguments": { "node": "drive0", "name": "pbs", "persistent": true } }

# full backup
{
  "execute": "drive-backup",
  "arguments": {
    "device": "drive0",
    "target": "file:/root/vm/backups/debian-full.qcow2",
    "sync": "full",
    "job-id": "fullbackup1"
  }
}

# clear bitmap
{
  "execute": "block-dirty-bitmap-clear",
  "arguments": { "node": "drive0", "name": "pbs" }
}

# make some changes inside VM
# dd if=/dev/urandom of=/root/1gb-random.img bs=1M count=1024 status=progress

# incremental backup
{
  "execute": "drive-backup",
  "arguments": {
    "device": "drive0",
    "target": "file:/root/vm/backups/debian-incr.qcow2",
    "sync": "incremental",
    "bitmap": "pbs",
    "job-id": "backup1"
  }
}

# reset bitmap after backup
{
  "execute": "block-dirty-bitmap-clear",
  "arguments": { "node": "drive0", "name": "pbs" }
}


As a result, I have two files:
Code:
root@localhost:~/vm/backups# ll
-rw-r--r-- 1 root root 3523280896 Nov 28 16:24 debian-full.qcow2
-rw-r--r-- 1 root root    5373952 Nov 28 16:25 debian-incr.qcow2

Then I use PBS client to back them up:
Code:
proxmox-backup-client backup debian.img:/root/vm/backups/debian-full.qcow2 --repository pasiu@pbs!token@127.0.0.1:bkup_pbs
proxmox-backup-client backup debian.img:/root/vm/backups/debian-incr.qcow2 --repository pasiu@pbs!token@127.0.0.1:bkup_pbs

When I restore the latest snapshot, hoping to get a “ready-to-use” qcow2 image:
Code:
proxmox-backup-client snapshots --repository pasiu@pbs!token@127.0.0.1:bkup_pbs
proxmox-backup-client restore host/localhost/2025-11-28T16:27:02Z debian.img /root/vm/backups/restore.qcow2 --repository pasiu@pbs!token@127.0.0.1:bkup_pbs

The restored file has exactly the same size as debian-incr.qcow2. I was expecting a full, ready-to-use image.

I assume I’m missing something here. Could someone clarify:

- Does PBS automatically know which file is the “backing file” when backing up incremental qcow2 images?
- Is the way I create snapshots using QMP the correct approach for PBS incremental backups?

Any insights would be much appreciated!
 
Hi,
you don't need to do anything via QMP at all. When a backup is made, the Proxmox VE backup stack will automatically set up a dirty bitmap for the PBS backup target, and this does not depend on the image format. The bitmap is also preserved when migrating the VM, but it will be dropped when the VM is shut down or the disk resized.

Regarding persistent bitmaps that are also kept when shut down, there is an open feature request: https://bugzilla.proxmox.com/show_bug.cgi?id=3233
 
Hi Fiona,

Many thanks for your reply. I think I owe you an apology - I didn’t make it clear in my previous question that I cannot use Proxmox VE. I need to use the PBS client and PBS server directly.

I managed to figure out how to create incremental backups using dirty bitmaps. As a result, I now have three files:
  • debian.full.qcow2 (full backup),
  • debian.incr0.qcow2 (incremental backup #0),
  • debian.incr1.qcow2 (incremental backup #1).
I then backed up these files via the PBS client:
Code:
proxmox-backup-client backup debian.img:/root/vm/20251201_backups/debian.full.qcow2 --repository pasiu@pbs\!token@127.0.0.1:bkup_pbs
proxmox-backup-client backup debian.img:/root/vm/20251201_backups/debian.incr0.qcow2 --repository pasiu@pbs\!token@127.0.0.1:bkup_pbs
proxmox-backup-client backup debian.img:/root/vm/20251201_backups/debian.incr1.qcow2 --repository pasiu@pbs\!token@127.0.0.1:bkup_pbs

I listed the backups:
Code:
proxmox-backup-client snapshots --repository pasiu@pbs\!token@127.0.0.1:bkup_pbs

┌─────────────────────────────────────┬────────────┬───────────────────────┐
│ snapshot                            │       size │ files                 │
╞═════════════════════════════════════╪════════════╪═══════════════════════╡
│ host/localhost/2025-12-02T12:12:24Z │ 12.656 GiB │ debian.img index.json │
├─────────────────────────────────────┼────────────┼───────────────────────┤
│ host/localhost/2025-12-02T12:13:05Z │   25.5 MiB │ debian.img index.json │
├─────────────────────────────────────┼────────────┼───────────────────────┤
│ host/localhost/2025-12-02T12:13:14Z │  11.25 MiB │ debian.img index.json │
└─────────────────────────────────────┴────────────┴───────────────────────┘
And restored the latest one:
Code:
proxmox-backup-client restore host/localhost/2025-12-02T12:13:14Z debian.img /root/vm/backups/restore.qcow2 --repository pasiu@pbs\!token@127.0.0.1:bkup_pbs
Here’s my question: I was hoping that PBS could automatically provide a full VM image representing the state of “incremental backup #1.” However, I was mistaken. To restore the VM, I would need to download all backups (full, incr0, incr1) and manually “merge” them to obtain the desired image. I had expected that the PBS server could handle this automatically.

Is this possible? I know that Proxmox VE provides proper restore functionality for this, but I am using pure QEMU/KVM and cannot currently switch to Proxmox VE. I am trying to find a clean way to leverage PBS server features to use dirty bitmaps in backups, but I am not sure how to achieve this with the PBS client. I could not find any documentation on the topic.


----
Maybe someone will find this useful: steps to create a backup using a dirty bitmap.
Code:
#create an qcow2 image file, virutal size must match with the virtual size of original image!
qemu-img create -f qcow2 /root/vm/backups/debian.full.qcow2 13958643712


{ "execute": "qmp_capabilities" }
{ "execute": "query-block" }

{ "execute": "block-dirty-bitmap-remove", "arguments": { "node": "drive0", "name": "pbs" } }
{ "execute": "block-dirty-bitmap-add", "arguments": { "node": "drive0", "name": "pbs", "persistent": true } }



-- full backup

qemu-img create -f qcow2 /root/vm/backups/debian.full.qcow2 13958643712

{
  "execute": "blockdev-add",
  "arguments": {
    "node-name": "target0",
    "driver": "qcow2",
    "file": { "driver": "file", "filename": "/root/vm/backups/debian.full.qcow2" }
  }
}

{
  "execute": "transaction",
  "arguments": {
    "actions": [
      {
        "type": "blockdev-backup",
        "data": {
          "device": "drive0",
          "target": "target0",
          "sync": "full",
          "job-id": "job-bkup1"
        }
      },
      {
        "type": "block-dirty-bitmap-clear",
        "data": {
          "node": "drive0",
          "name": "pbs"
        }
      }
    ]
  }
}


{ "execute": "blockdev-del", "arguments": { "node-name": "target0" } }


## Each new incremental backup re-synchronizes the bitmap to the latest backup authored, allowing a user to continue to "consume" it to create new backups on top of an existing chain.

-- inc 0 backup

qemu-img create -f qcow2 -b /root/vm/backups/debian.full.qcow2 -F qcow2 /root/vm/backups/debian.incr0.qcow2


{
  "execute": "blockdev-add",
  "arguments": {
    "node-name": "target-incr0",
    "driver": "qcow2",
    "file": {
      "driver": "file",
      "filename": "/root/vm/backups/debian.incr0.qcow2"
    }
  }
}

{
  "execute": "blockdev-backup",
  "arguments": {
    "device": "drive0",
    "target": "target-incr0",
    "sync": "incremental",
    "bitmap": "pbs",
    "job-id": "job-bkup-incr0"
  }
}

{ "execute": "blockdev-del", "arguments": { "node-name": "target-incr0" } }



-- inc 1 backup

qemu-img create -f qcow2 -b /root/vm/backups/debian.incr0.qcow2 -F qcow2 /root/vm/backups/debian.incr1.qcow2


{
  "execute": "blockdev-add",
  "arguments": {
    "node-name": "target-incr1",
    "driver": "qcow2",
    "file": {
      "driver": "file",
      "filename": "/root/vm/backups/debian.incr1.qcow2"
    }
  }
}

{
  "execute": "blockdev-backup",
  "arguments": {
    "device": "drive0",
    "target": "target-incr1",
    "sync": "incremental",
    "bitmap": "pbs",
    "job-id": "job-bkup-incr1"
  }
}

{ "execute": "blockdev-del", "arguments": { "node-name": "target-incr1" } }
 
Okay, I see. You could upload the full image each time. The server will deduplicate known chunks by itself. PBS gives you this advantage for free.

I don't think this is easily possible to do proper incremental backup without any additional infrastructure, as the code for all that is integrated in our downstream version of QEMU and a shared library for it:
Main patch for QEMU: https://git.proxmox.com/?p=pve-qemu...4;hb=ae1309d4df5ca9b6b62122d0f75bbae229856f48
Library which also uses PBS internals: https://git.proxmox.com/?p=proxmox-...9;hb=2b8ebba0cffecd479f296aaad82d8bec89355599

If you really would like to implement your own client for PBS to handle this, but this is very much out of scope for the forum here, see the backup API:
https://pbs.proxmox.com/docs/api-viewer/index.html#/backup/_upgrade_
And the register_image function (downloads previous fixed index so that later we can send along only new chunks for incremental backup):
https://git.proxmox.com/?p=proxmox-...2b8ebba0cffecd479f296aaad82d8bec89355599#l182

And not sure you're the first one, maybe there already are examples of using the backup API for PBS like this in the wild.
 
  • Like
Reactions: Pasiu
Many thanks for your reply – it explains a lot :)
Yep, a full backup is an option, but I wanted to avoid this and make use of incremental backups provided by dirty bitmaps.

Ok, so the PBS client works as expected. I was hoping to get this functionality out of the box, but I was wrong ;)

At first glance, this manual work seems a bit too complicated for me; nevertheless, I will get familiar with the provided links.

Thanks once again! Have a good day.