QCOW2 Rebase and Commit Change Different Files
Last edited on August 12, 2026

qemu-img rebase and qemu-img commit can both shorten a QCOW2 backing chain, but they write in opposite directions. A safe rebase changes the top image so it can inherit from a different backing file without changing the guest-visible bytes. A commit changes a backing image by copying overlay data downward. Unsafe rebase changes only backing metadata and preserves data only when the old and new backing content already match.

That file ownership is the decision. Syntax should come second. The worked lab below builds two independent three-layer chains, places distinct byte patterns in Base, Middle, and Top, then proves the complete virtual-disk hash before and after each operation. A third disposable copy makes an incorrect unsafe rebase fail visibly instead of leaving the risk as a warning.

Reproduction ran on Debian 13 with QEMU qemu-img and qemu-io 10.0.11. Every image lives under the exact marker-owned path /tmp/voxfor-qcow2-backing-chain-lab. No VM, block device, filesystem, libvirt definition, or production image is opened. This article is for KVM, QEMU, libvirt, or Proxmox operators who already understand offline maintenance and need a precise mutation receipt before changing a real chain.

Choose the File That Is Allowed to Change

Read a chain as Base <- Middle <- Top: Top is the image a guest would open, Middle backs Top, and Base backs Middle. An unallocated cluster in Top is read from Middle; an unallocated cluster in Middle is read from Base. The current QEMU disk image utility reference warns never to modify an image used by a running VM or another process. The commands in this lab are therefore offline operations on disposable files.

Operation File that receives data or metadata Guest-view promise Main precondition
Safe rebase Top directly to Base Top Keeps Top’s guest-visible content unchanged Old chain and new backing must both be readable
commit Top into its immediate backing Middle, while Top is emptied after success Current Top view remains readable through the modified backing Top and backing are offline; rollback copy exists
Unsafe rebase -u Top to another backing Top metadata only No preservation promise New backing is already byte-equivalent where Top inherits
convert Top to a standalone image New destination Destination represents the current guest view Enough destination space and separate acceptance

Libvirt’s backing-chain guide adds two practical rules: record the backing format with -F instead of relying on format probing, and use trusted absolute backing paths when automation needs a stable identity. A file appearing in ls is not enough; the image header, actual resolved path, format, and full chain must agree.

Safe rebase and commit write to different QCOW2 filesTwo backing chains compare mutation direction. Safe rebase copies required clusters into the top image and points it directly to the base while the middle file remains unchanged. Commit copies top-layer data into the immediate middle backing file while the base remains unchanged.SAFE REBASErequired clusters copied into TopBaseMiddleTopunchanged, detachedCOMMITTop data copied into MiddleBaseMiddleTopchangedemptied
Safe rebase writes preserved differences into Top and detaches Middle; commit writes Top data into the immediate backing file.

One visual is useful here because the two arrows encode the decision faster than another paragraph. It is not execution evidence. The tested hashes, pattern reads, chain JSON, and image checks below prove the operations.

Build Two Chains With Distinct Inherited Bytes

Each 1 MiB virtual disk contains three non-overlapping 64 KiB patterns: 0x11 in Base, 0x22 in Middle, and 0x33 in Top. Reading Top must therefore traverse all three layers. Separate rebase-* and commit-* chains keep one operation from contaminating the other’s evidence.

Bootstrap refuses a pre-existing directory, writes the marker before creating images, uses absolute backing paths, and records qcow2 explicitly for every backing file.

set -Eeuo pipefail
LAB=/tmp/voxfor-qcow2-backing-chain-lab
MARKER="$LAB/.voxfor-qcow2-backing-chain-lab"
[[ ! -e "$LAB" ]]
command -v qemu-img qemu-io jq sha256sum >/dev/null
install -d -m 0700 "$LAB"
printf 'VOXFOR_QCOW2_BACKING_CHAIN_LAB\n' > "$MARKER"
for PREFIX in rebase commit; do
  qemu-img create -q -f qcow2 "$LAB/${PREFIX}-base.qcow2" 1M
  qemu-io -f qcow2 -c 'write -P 0x11 0 64k' \
    "$LAB/${PREFIX}-base.qcow2" >/dev/null
  qemu-img create -q -f qcow2 -F qcow2 \
    -b "$LAB/${PREFIX}-base.qcow2" "$LAB/${PREFIX}-middle.qcow2"
  qemu-io -f qcow2 -c 'write -P 0x22 64k 64k' \
    "$LAB/${PREFIX}-middle.qcow2" >/dev/null
  qemu-img create -q -f qcow2 -F qcow2 \
    -b "$LAB/${PREFIX}-middle.qcow2" "$LAB/${PREFIX}-top.qcow2"
  qemu-io -f qcow2 -c 'write -P 0x33 128k 64k' \
    "$LAB/${PREFIX}-top.qcow2" >/dev/null
done
[[ "$(<"$MARKER")" == VOXFOR_QCOW2_BACKING_CHAIN_LAB ]]
printf 'fixture=created qemu=%s\n' "$(qemu-img --version | head -n 1)"

Record the chain and the complete guest view

qemu-img info --backing-chain --output=json returns every layer in resolution order. qemu-img map shows which file supplies allocated ranges. Neither command alone proves the virtual bytes, so the baseline also converts each Top image to raw and stores a SHA-256 hash of the complete 1 MiB guest view.

set -Eeuo pipefail
LAB=/tmp/voxfor-qcow2-backing-chain-lab
MARKER="$LAB/.voxfor-qcow2-backing-chain-lab"
[[ "$LAB" == /tmp/voxfor-qcow2-backing-chain-lab ]]
[[ -f "$MARKER" ]]
[[ "$(<"$MARKER")" == VOXFOR_QCOW2_BACKING_CHAIN_LAB ]]
for PREFIX in rebase commit; do
  TOP="$LAB/${PREFIX}-top.qcow2"
  [[ "$(qemu-img info --output=json --backing-chain "$TOP" | jq 'length')" == 3 ]]
  qemu-io -f qcow2 -c 'read -P 0x11 0 64k' "$TOP" >/dev/null
  qemu-io -f qcow2 -c 'read -P 0x22 64k 64k' "$TOP" >/dev/null
  qemu-io -f qcow2 -c 'read -P 0x33 128k 64k' "$TOP" >/dev/null
  qemu-img map --output=json "$TOP" > "$LAB/${PREFIX}-map-before.json"
  qemu-img convert -q -f qcow2 -O raw "$TOP" "$LAB/${PREFIX}-before.raw"
  sha256sum "$LAB/${PREFIX}-before.raw" | awk '{print $1}' \
    > "$LAB/${PREFIX}-view-before.sha256"
  sha256sum "$LAB/${PREFIX}-base.qcow2" | awk '{print $1}' \
    > "$LAB/${PREFIX}-base-before.sha256"
  sha256sum "$LAB/${PREFIX}-middle.qcow2" | awk '{print $1}' \
    > "$LAB/${PREFIX}-middle-before.sha256"
  rm -f -- "$LAB/${PREFIX}-before.raw"
done
printf 'baseline rebase_layers=3 commit_layers=3 view_hash=%s\n' \
  "$(<"$LAB/rebase-view-before.sha256")"

Pattern checks distinguish inheritance from mere file readability. If 0x22 cannot be read through Top, the chain already fails before any rebase or commit and the maintenance window should stop.

For storage-capacity work rather than chain surgery, the QCOW2 allocation and sparse-copy workflow separates virtual size, host allocation, discard, and a source-preserving copy. Rebase and commit should not be used as generic space-reclamation commands.

Safe Rebase Copies Differences Into Top

Safe rebase points rebase-top.qcow2 directly to rebase-base.qcow2, skipping Middle. QEMU compares the old and new backing views and copies any required differences into Top before changing the backing relationship. The official reference describes that guest-visible preservation contract; it also makes clear why the old backing chain must remain available while the operation runs.

This block records four independent facts: the chain drops from three layers to two, the complete guest hash stays identical, both former backing files keep their host-file hashes, and the 0x22 bytes formerly supplied by Middle remain readable through Top.

set -Eeuo pipefail
LAB=/tmp/voxfor-qcow2-backing-chain-lab
MARKER="$LAB/.voxfor-qcow2-backing-chain-lab"
[[ "$LAB" == /tmp/voxfor-qcow2-backing-chain-lab ]]
[[ "$(<"$MARKER")" == VOXFOR_QCOW2_BACKING_CHAIN_LAB ]]
TOP="$LAB/rebase-top.qcow2"
BASE="$LAB/rebase-base.qcow2"
MIDDLE="$LAB/rebase-middle.qcow2"
VIEW_BEFORE="$(<"$LAB/rebase-view-before.sha256")"
BASE_BEFORE="$(<"$LAB/rebase-base-before.sha256")"
MIDDLE_BEFORE="$(<"$LAB/rebase-middle-before.sha256")"
qemu-img rebase -q -f qcow2 -F qcow2 -b "$BASE" "$TOP"
qemu-img convert -q -f qcow2 -O raw "$TOP" "$LAB/rebase-after.raw"
VIEW_AFTER="$(sha256sum "$LAB/rebase-after.raw" | awk '{print $1}')"
rm -f -- "$LAB/rebase-after.raw"
[[ "$VIEW_AFTER" == "$VIEW_BEFORE" ]]
[[ "$(sha256sum "$BASE" | awk '{print $1}')" == "$BASE_BEFORE" ]]
[[ "$(sha256sum "$MIDDLE" | awk '{print $1}')" == "$MIDDLE_BEFORE" ]]
[[ "$(qemu-img info --output=json --backing-chain "$TOP" | jq 'length')" == 2 ]]
qemu-io -f qcow2 -c 'read -P 0x22 64k 64k' "$TOP" >/dev/null
printf 'safe_rebase=accepted layers=2 guest_hash_unchanged=yes backing_files_unchanged=yes\n'

Middle is now detached from this Top chain, but detached does not mean deletable. Another overlay, backup catalog, VM definition, or retention policy may still reference it. Search every relevant libvirt XML, inventory, backup manifest, and image header before retiring a layer. The libvirt guide for merging image chains uses separate live block operations because an active VM requires coordinated block jobs and a pivot; an offline qemu-img rebase is not a substitute for that live workflow.

Commit Writes Down Into the Backing File

Commit uses an independent chain. Running qemu-img commit commit-top.qcow2 copies Top’s allocated data into its immediate backing file, commit-middle.qcow2. QEMU then empties the committed Top after success. Because Top still points to Middle, its complete guest view remains readable, but the backing file has changed.

set -Eeuo pipefail
LAB=/tmp/voxfor-qcow2-backing-chain-lab
MARKER="$LAB/.voxfor-qcow2-backing-chain-lab"
[[ "$LAB" == /tmp/voxfor-qcow2-backing-chain-lab ]]
[[ "$(<"$MARKER")" == VOXFOR_QCOW2_BACKING_CHAIN_LAB ]]
TOP="$LAB/commit-top.qcow2"
BASE="$LAB/commit-base.qcow2"
MIDDLE="$LAB/commit-middle.qcow2"
VIEW_BEFORE="$(<"$LAB/commit-view-before.sha256")"
BASE_BEFORE="$(<"$LAB/commit-base-before.sha256")"
MIDDLE_BEFORE="$(<"$LAB/commit-middle-before.sha256")"
qemu-img commit -q -f qcow2 "$TOP"
qemu-img convert -q -f qcow2 -O raw "$TOP" "$LAB/commit-after.raw"
VIEW_AFTER="$(sha256sum "$LAB/commit-after.raw" | awk '{print $1}')"
rm -f -- "$LAB/commit-after.raw"
[[ "$VIEW_AFTER" == "$VIEW_BEFORE" ]]
[[ "$(sha256sum "$BASE" | awk '{print $1}')" == "$BASE_BEFORE" ]]
[[ "$(sha256sum "$MIDDLE" | awk '{print $1}')" != "$MIDDLE_BEFORE" ]]
qemu-io -f qcow2 -c 'read -P 0x33 128k 64k' "$MIDDLE" >/dev/null
printf 'commit=accepted guest_hash_unchanged=yes immediate_backing_changed=yes base_unchanged=yes\n'

Rollback follows the mutation direction. Before commit, the backing file represents an older point in time. After commit, it contains the overlay’s changes. Preserve a verified copy or storage snapshot of every file that the operation may modify, and keep it outside the chain being changed. A snapshot name alone does not prove application-consistent recovery; the Proxmox snapshot-backup consistency guide explains the separate guest coordination and restore acceptance needed for that claim.

Offline commit is not live block-commit

qemu-img commit modifies an offline image chain. A running QEMU process needs QMP or libvirt coordination. QEMU’s live block operations documentation distinguishes block-commit, block-stream, mirror, and backup; active commit also requires a final pivot or cancel decision. Never infer that an offline command is safe merely because a related live primitive exists.

Once a live or offline chain is consolidated, recovery still depends on a usable restored guest. A timed Proxmox restore rehearsal checks identity isolation, application behavior, and actual recovery time instead of treating image manipulation as disaster recovery by itself.

Make Unsafe Rebase Break a Disposable Copy

Unsafe rebase with -u rewrites backing metadata without comparing old and new backing content. That is appropriate only when the backing file moved or was replaced by a byte-equivalent object and the operator has independently proved equivalence. It is not a faster form of safe rebase.

This negative control creates a Good backing file with 0x44, a Wrong backing file with 0x55, and a Top that inherits the first range. Repointing Top to Wrong changes the complete guest hash and makes 0x55 visible. Repointing the disposable copy back to Good restores the original hash.

set -Eeuo pipefail
LAB=/tmp/voxfor-qcow2-backing-chain-lab
MARKER="$LAB/.voxfor-qcow2-backing-chain-lab"
[[ "$LAB" == /tmp/voxfor-qcow2-backing-chain-lab ]]
[[ "$(<"$MARKER")" == VOXFOR_QCOW2_BACKING_CHAIN_LAB ]]
GOOD="$LAB/unsafe-good.qcow2"
WRONG="$LAB/unsafe-wrong.qcow2"
TOP="$LAB/unsafe-top.qcow2"
qemu-img create -q -f qcow2 "$GOOD" 1M
qemu-io -f qcow2 -c 'write -P 0x44 0 64k' "$GOOD" >/dev/null
qemu-img create -q -f qcow2 "$WRONG" 1M
qemu-io -f qcow2 -c 'write -P 0x55 0 64k' "$WRONG" >/dev/null
qemu-img create -q -f qcow2 -F qcow2 -b "$GOOD" "$TOP"
qemu-io -f qcow2 -c 'write -P 0x66 64k 64k' "$TOP" >/dev/null
qemu-img convert -q -f qcow2 -O raw "$TOP" "$LAB/unsafe-before.raw"
BEFORE="$(sha256sum "$LAB/unsafe-before.raw" | awk '{print $1}')"
rm -f -- "$LAB/unsafe-before.raw"
qemu-img rebase -q -u -f qcow2 -F qcow2 -b "$WRONG" "$TOP"
qemu-img convert -q -f qcow2 -O raw "$TOP" "$LAB/unsafe-wrong.raw"
AFTER_WRONG="$(sha256sum "$LAB/unsafe-wrong.raw" | awk '{print $1}')"
rm -f -- "$LAB/unsafe-wrong.raw"
[[ "$AFTER_WRONG" != "$BEFORE" ]]
qemu-io -f qcow2 -c 'read -P 0x55 0 64k' "$TOP" >/dev/null
qemu-img rebase -q -u -f qcow2 -F qcow2 -b "$GOOD" "$TOP"
qemu-img convert -q -f qcow2 -O raw "$TOP" "$LAB/unsafe-restored.raw"
[[ "$(sha256sum "$LAB/unsafe-restored.raw" | awk '{print $1}')" == "$BEFORE" ]]
rm -f -- "$LAB/unsafe-restored.raw"
printf 'unsafe_rebase_control=changed_guest_view wrong_backing_detected=yes restored_copy=yes\n'

For a moved backing file, compare more than pathname and host-file size. Retain the old and proposed objects, resolve their formats, compare hashes when they are meant to be identical files, and prove the full guest-view hash on a copied Top before touching the production header. The Debian qemu-img manual repeats the offline-use warning and documents -u as unsafe because the command does not verify the backing content.

Turn the Lab Into a Production Decision

Here is the representative receipt from the complete disposable run:

environment: Debian 13; qemu-img 10.0.11; qemu-io 10.0.11
fixture: two independent Base <- Middle <- Top chains; 1 MiB each
baseline: rebase_layers=3; commit_layers=3
guest_view_sha256: 081c637900da5f63f8c283c210febaea16a01c9c10f287d9143848c4ac1ae8df
safe_rebase: layers=2; guest_hash_unchanged=yes; backing_files_unchanged=yes
commit: guest_hash_unchanged=yes; immediate_backing_changed=yes; base_unchanged=yes
unsafe_control: wrong backing changed guest view; correct backing restored original hash
verification: nine QCOW2 images checked; rebase and commit guest views retained
cleanup: exact marker-owned path absent

Use safe rebase when Top must keep the same virtual bytes while its backing dependency changes. Use commit when the explicit goal is to move Top’s changes into a backing file and that backing file is allowed to lose its older point-in-time meaning. Use unsafe rebase only for a proven-equivalent backing relocation. Choose a standalone convert when the operational goal is an independent image rather than another dependency relationship.

Before any production operation, capture:

  1. The VM or process that owns the active disk and proof it is stopped, unless a documented live block job is used.
  2. qemu-img info --backing-chain --output=json with resolved absolute paths and explicit formats.
  3. Host-file identities and checksums for the exact files expected to remain unchanged.
  4. A guest-view manifest from a preserved copy, plus application or filesystem acceptance appropriate to the workload.
  5. Free-space headroom for safe rebase’s copied clusters and a rollback copy outside the mutation set.
  6. Every consumer of a layer: VM XML, other overlays, backup catalogs, replicas, retention automation, and restore procedures.

Storage integrity and guest integrity are different. qemu-img check validates QCOW2 metadata consistency; it does not validate ext4, XFS, NTFS, PostgreSQL, or application transactions inside the virtual disk. When the guest filesystem itself is forced read-only, use the offline filesystem evidence and repair boundary rather than assuming a clean QCOW2 header proves the mounted data healthy.

The operation is acceptable when every intended image is offline or controlled by the correct live block job; the recorded chain and backing formats match the planned topology; qemu-img check reports no image errors; the complete Top guest-view hash and all three pattern assertions match the pre-operation manifest; only the file named by the selected mutation direction changes; detached layers remain preserved until every consumer is checked; and an isolated guest or application-level test confirms the workload state required by the maintenance objective.

Validate every image, not only Top

Final acceptance checks all nine lab images, re-creates both complete guest-view hashes, and requires the exact expected image count. A glob with no count assertion could silently skip a missing layer.

set -Eeuo pipefail
LAB=/tmp/voxfor-qcow2-backing-chain-lab
MARKER="$LAB/.voxfor-qcow2-backing-chain-lab"
[[ "$LAB" == /tmp/voxfor-qcow2-backing-chain-lab ]]
[[ "$(<"$MARKER")" == VOXFOR_QCOW2_BACKING_CHAIN_LAB ]]
CHECKED=0
while IFS= read -r IMAGE; do
  qemu-img check -q -f qcow2 "$IMAGE"
  CHECKED=$((CHECKED + 1))
done < <(find "$LAB" -maxdepth 1 -type f -name '*.qcow2' -print | sort)
[[ "$CHECKED" == 9 ]]
for PREFIX in rebase commit; do
  qemu-img convert -q -f qcow2 -O raw \
    "$LAB/${PREFIX}-top.qcow2" "$LAB/${PREFIX}-verify.raw"
  ACTUAL="$(sha256sum "$LAB/${PREFIX}-verify.raw" | awk '{print $1}')"
  rm -f -- "$LAB/${PREFIX}-verify.raw"
  [[ "$ACTUAL" == "$(<"$LAB/${PREFIX}-view-before.sha256")" ]]
done
printf 'verification=accepted images_checked=%s rebase_view=yes commit_view=yes\n' "$CHECKED"

A dedicated virtualization host can also fail because of memory pressure rather than storage lineage. The KVM balloon target and guest-pressure workflow provides the two-sided host/guest acceptance needed before changing memory allocation; it should not be folded into a disk-chain maintenance decision.

FAQ: Backing-Chain Decisions

Does safe rebase modify the new backing file?

No. In safe mode, QEMU preserves the guest-visible content of Top by copying required differences into Top before changing its backing relationship. The tested Base and Middle host-file hashes stayed unchanged. Confirm that behavior on a copy with the actual formats and QEMU version used in production.

What file does qemu-img commit top.qcow2 change?

When -b is omitted, the command commits changes from Top into the immediate backing file of Top. In the lab, Middle changed, Base did not, and the Top guest-view hash stayed the same. A deeper -b BASE target changes the selected backing layer and needs its own complete-chain rehearsal.

Can I delete the middle image after safe rebase?

Not solely because this Top no longer references it. Another overlay, VM definition, backup catalog, or restore point may still depend on Middle. Keep the file until a full consumer inventory proves it is unreferenced and the rollback window has closed.

When is unsafe rebase appropriate?

Unsafe rebase is appropriate when the backing location changes but the new object is already equivalent to the old backing content. Prove that equivalence and the guest-view manifest of the copied Top first. If the backing contents differ, use safe rebase or create a standalone converted image.

Does qemu-img check prove the guest filesystem is healthy?

No. It checks supported disk-image metadata and consistency. Guest filesystems, databases, and applications require their own offline or isolated acceptance tests. A successful image check cannot establish application-consistent recovery.

Should I use qemu-img on an active VM disk?

No. QEMU explicitly warns against modifying images in use by a running VM or another process. Use a supported live block job from the hypervisor with explicit base, top, completion, and pivot handling, or shut the VM down and prove ownership before an offline operation.

Remove the Fixture After Keeping the Receipt

Cleanup validates the exact path and marker, deletes only regular files at one directory level, removes the now-empty directory, and asserts absence. It deliberately avoids a broad recursive target.

set -Eeuo pipefail
LAB=/tmp/voxfor-qcow2-backing-chain-lab
MARKER="$LAB/.voxfor-qcow2-backing-chain-lab"
[[ "$LAB" == /tmp/voxfor-qcow2-backing-chain-lab ]]
[[ -f "$MARKER" ]]
[[ "$(<"$MARKER")" == VOXFOR_QCOW2_BACKING_CHAIN_LAB ]]
find "$LAB" -mindepth 1 -maxdepth 1 -type f -delete
rmdir -- "$LAB"
[[ ! -e "$LAB" ]]
printf 'cleanup=accepted path_absent=yes\n'

If production acceptance changes after rebase or commit, stop before deleting any detached layer, preserve the failed chain metadata and output, and restore only the backed-up image files plus VM disk reference that belonged to the recorded pre-operation topology. Re-run qemu-img info --backing-chain and qemu-img check on copies before starting the VM, then perform the same isolated guest and application acceptance. Do not improvise rebase -u against a different backing file, commit another layer, delete an “unused” overlay, or repair the guest filesystem until the mutation owner is proved.

Retain a receipt naming QEMU version, maintenance mode, resolved chain, formats, host-file hashes, full guest-view manifest, operation and target, changed-file list, image checks, application acceptance, detached-layer consumers, rollback result, and cleanup boundary. That evidence makes the next decision reviewable; the command name alone does not.

Share this Post

Leave a Reply

Your email address will not be published. Required fields are marked *