Go to file
Stefan Hacker a08efb3795 Chunked upload for large OVAs
The Convert tab now slices the OVA into 16 MiB chunks and PUTs them
with an offset parameter instead of one multipart POST. Avoids the
temp-file double-copy and tmpfs exhaustion that broke uploads of very
large (>50 GiB) appliances.

- POST /api/uploads creates an upload session, GET/PUT/DELETE manage
  a partial file written directly in upload_dir (seek+write, no copy)
- POST /api/jobs now accepts JSON {upload_id, vmid, storage} to
  finalize the chunked upload; multipart form path kept for CLI/curl
- Client: File.slice() loop with per-chunk XHR, progress bar driven by
  bytes sent / total, partial files resumable via upload_id

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 13:27:57 +02:00
scripts Add ova2vzdump: OVA -> Proxmox vzdump (.vma.zst) converter 2026-04-21 12:50:04 +02:00
src/ova2vzdump Chunked upload for large OVAs 2026-04-21 13:27:57 +02:00
.dockerignore Add ova2vzdump: OVA -> Proxmox vzdump (.vma.zst) converter 2026-04-21 12:50:04 +02:00
.gitignore first commit 2026-04-21 12:49:04 +02:00
Dockerfile Add ova2vzdump: OVA -> Proxmox vzdump (.vma.zst) converter 2026-04-21 12:50:04 +02:00
README.md Add ova2vzdump: OVA -> Proxmox vzdump (.vma.zst) converter 2026-04-21 12:50:04 +02:00
docker-compose.yml Add ova2vzdump: OVA -> Proxmox vzdump (.vma.zst) converter 2026-04-21 12:50:04 +02:00
pyproject.toml Add ova2vzdump: OVA -> Proxmox vzdump (.vma.zst) converter 2026-04-21 12:50:04 +02:00
requirements.txt Add ova2vzdump: OVA -> Proxmox vzdump (.vma.zst) converter 2026-04-21 12:50:04 +02:00

README.md

ova2vzdump

English · Deutsch

Convert OVA appliances (VirtualBox / VMware export) into Proxmox vzdump .vma.zst backup files that can be restored with qmrestore.

The tricky part is vma, the proprietary container format Proxmox uses for QEMU backups. The vma binary only ships with Proxmox, so this tool runs inside a Docker image that installs pve-qemu-kvm from the Proxmox no-subscription apt repo. That way you can convert appliances on any machine with Docker — no PVE host required.

Pipeline

  1. Extract OVA tar → OVF descriptor + VMDK disk(s)
  2. Parse OVF XML → CPU, memory, disks, NICs
  3. qemu-img convert each VMDK → raw
  4. Synthesize a qemu-server.conf
  5. vma create packs raws + conf → .vma
  6. zstd compresses → vzdump-qemu-<vmid>-<ts>.vma.zst

Usage

Build the image

docker compose build

Web UI

docker compose up
# browse http://localhost:8880

Uploads land in ./data/uploads, results in ./data/output.

Getting a test OVA

ova2vzdump ships a built-in test OVA generator — both the CLI and the web UI have a dedicated entry point. Two modes:

  • stub (default, ~1 KB): valid OVA with an empty VMDK. Fast, exercises the full extract → parse → qemu-img → vma → zstd pipeline. Not bootable.
  • bootable (~80 MB): packages a pre-cached Alpine Linux raw image (downloaded during docker build, sha512-verified). After qmrestore on Proxmox the VM actually boots to an Alpine login prompt — this is what you want for a real end-to-end test.

Existing test OVAs are reused by default; pass --force to rebuild.

CLI

# stub:
docker compose run --rm ova2vzdump create-test-ova \
    /data/uploads/test.ova --name demo

# bootable Alpine:
docker compose run --rm ova2vzdump create-test-ova \
    /data/uploads/alpine.ova --name alpine --bootable

# rebuild:
docker compose run --rm ova2vzdump create-test-ova \
    /data/uploads/test.ova --force

Web UI

Click the Create test OVA tab, pick a name, tick Bootable if you want real Alpine, hit Create. The list at the bottom shows what's already in the upload directory and offers direct download. The generated OVA also appears as an available input on the Convert tab.

If you'd rather grab a real-world appliance from elsewhere:

  • TurnKey Linux Core (~250 MB): turnkeylinux.org/core → VM build.
  • DIY: export any small VM from VirtualBox via File → Export Appliance….ova.

CLI

docker run --rm \
    -v "$PWD/data/uploads:/in" \
    -v "$PWD/data/output:/out" \
    ova2vzdump:latest convert /in/appliance.ova /out \
        --vmid 123 --storage local-lvm

Output filename looks like vzdump-qemu-123-2026_04_21-12_30_00.vma.zst.

Restore on Proxmox

Copy the .vma.zst onto a PVE node into a backup-capable storage (for example /var/lib/vz/dump/), then:

qmrestore vzdump-qemu-123-2026_04_21-12_30_00.vma.zst 123 --storage local-lvm

Options

Flag Meaning Default
--vmid Placeholder VMID — overridden at restore time 100
--storage Placeholder storage name in qemu-server.conf — overridden by qmrestore --storage local-lvm
--keep-workdir Keep intermediate raw disks + vma for debugging off

qmrestore <file> <target-vmid> [--storage <name>] always decides the real VMID and storage. The values baked into the archive are only placeholders; you can leave the defaults for most use cases and only tweak them if you want a meaningful filename like vzdump-qemu-<originally-intended-vmid>-*.vma.zst.

Config mapping

OVF qemu-server.conf
VirtualHardwareSection CPU quantity cores
VirtualHardwareSection memory memory (MiB)
OS description heuristic (linux/windows/…) ostype (l26 / win10 / other)
Disks w/ SCSI controller scsiN + scsihw: virtio-scsi-pci
Disks w/ SATA controller sataN
Disks w/ IDE controller ideN
Ethernet subtype netN: <model>=auto,bridge=vmbr0

Heuristics are conservative; review the generated config after restore before booting production workloads.

Limitations

  • OVF 1.x / 2.x only (DMTF envelope namespace).
  • manifest, cert, and signed OVAs are not verified.
  • Firmware (BIOS vs. UEFI) is not read from OVF — defaults to SeaBIOS. Edit the config after restore if your appliance needs OVMF.
  • MAC addresses are not preserved (Proxmox assigns new ones on import).

Local development (without Docker)

You need qemu-img, vma, and zstd on your PATH — i.e. you must be on a Proxmox host. Then:

pip install -e .
ova2vzdump convert appliance.ova ./out

ova2vzdump (Deutsch)

Konvertiert OVA-Appliances (Export aus VirtualBox / VMware) in Proxmox vzdump .vma.zst-Backup-Dateien, die sich mit qmrestore direkt zurückspielen lassen.

Der knifflige Teil ist vma — das proprietäre Container-Format, in dem Proxmox seine QEMU-Backups speichert. Die vma-Binary gibt es nur mit Proxmox, deshalb läuft dieses Tool in einem Docker-Image, das pve-qemu-kvm aus dem Proxmox-No-Subscription-Apt-Repo installiert. Dadurch kannst du Appliances auf jeder Maschine mit Docker konvertieren — ganz ohne PVE-Host.

Pipeline

  1. OVA-Tar entpacken → OVF-Descriptor + VMDK-Disk(s)
  2. OVF-XML parsen → CPU, RAM, Disks, NICs
  3. Jede VMDK mit qemu-img convert nach raw konvertieren
  4. Eine qemu-server.conf generieren
  5. vma create packt raws + config → .vma
  6. zstd komprimiert → vzdump-qemu-<vmid>-<ts>.vma.zst

Benutzung

Image bauen

docker compose build

Web-UI

docker compose up
# dann http://localhost:8880 öffnen

Uploads landen in ./data/uploads, Ergebnisse in ./data/output.

Test-OVA bekommen

ova2vzdump bringt einen eingebauten Test-OVA-Generator mit — sowohl die CLI als auch die Web-UI haben einen eigenen Einstiegspunkt. Zwei Modi:

  • stub (Default, ~1 KB): valide OVA mit leerer VMDK. Schnell, testet die komplette Pipeline extract → parse → qemu-img → vma → zstd. Nicht bootbar.
  • bootable (~80 MB): verpackt ein vorgecachtes Alpine-Linux-Raw- Image (wird beim docker build heruntergeladen, sha512-verifiziert). Nach qmrestore auf Proxmox bootet die VM tatsächlich in einen Alpine-Login — das ist der echte End-to-End-Test.

Vorhandene Test-OVAs werden standardmäßig wiederverwendet; --force baut neu.

CLI

# Stub:
docker compose run --rm ova2vzdump create-test-ova \
    /data/uploads/test.ova --name demo

# Bootbares Alpine:
docker compose run --rm ova2vzdump create-test-ova \
    /data/uploads/alpine.ova --name alpine --bootable

# Neu bauen:
docker compose run --rm ova2vzdump create-test-ova \
    /data/uploads/test.ova --force

Web-UI

Auf den Tab Create test OVA klicken, Name wählen, Häkchen bei Bootable setzen wenn du echtes Alpine willst, auf Create klicken. Die Liste unten zeigt, was im Upload-Verzeichnis liegt, mit direkten Download-Buttons. Die erzeugte OVA erscheint auch im Convert-Tab als auswählbare Eingabe.

Lieber eine echte Appliance von woanders?

  • TurnKey Linux Core (~250 MB): turnkeylinux.org/core → VM-Build.
  • DIY: irgendeine kleine VM aus VirtualBox exportieren via Datei → Appliance exportieren….ova.

CLI (Konvertierung)

docker run --rm \
    -v "$PWD/data/uploads:/in" \
    -v "$PWD/data/output:/out" \
    ova2vzdump:latest convert /in/appliance.ova /out \
        --vmid 123 --storage local-lvm

Der Ausgabedateiname sieht etwa so aus: vzdump-qemu-123-2026_04_21-12_30_00.vma.zst.

Restore auf Proxmox

Die .vma.zst auf einen PVE-Node in ein backup-fähiges Storage kopieren (z.B. /var/lib/vz/dump/), dann:

qmrestore vzdump-qemu-123-2026_04_21-12_30_00.vma.zst 123 --storage local-lvm

Optionen

Flag Bedeutung Default
--vmid Platzhalter-VMID — wird beim Restore überschrieben 100
--storage Platzhalter-Storage-Name in qemu-server.conf — wird durch qmrestore --storage überschrieben local-lvm
--keep-workdir Zwischenergebnisse (raw-Disks, vma) behalten, zum Debuggen aus

qmrestore <file> <ziel-vmid> [--storage <name>] entscheidet immer die echte VMID und das Storage. Die Werte im Archiv sind nur Platzhalter — Defaults reichen meist. Du passt sie höchstens an, wenn du einen sprechenden Dateinamen haben willst, z.B. vzdump-qemu-<ursprüngliche-vmid>-*.vma.zst.

Config-Mapping

OVF qemu-server.conf
VirtualHardwareSection CPU-Anzahl cores
VirtualHardwareSection RAM memory (MiB)
OS-Description-Heuristik (linux/windows/…) ostype (l26 / win10 / other)
Disks am SCSI-Controller scsiN + scsihw: virtio-scsi-pci
Disks am SATA-Controller sataN
Disks am IDE-Controller ideN
Ethernet-Subtyp netN: <model>=auto,bridge=vmbr0

Die Heuristiken sind konservativ; bitte die generierte Config nach dem Restore einmal prüfen, bevor du produktive Workloads startest.

Limitierungen

  • Nur OVF 1.x / 2.x (DMTF-Envelope-Namespace).
  • manifest, cert und signierte OVAs werden nicht verifiziert.
  • Firmware (BIOS vs. UEFI) wird nicht aus der OVF gelesen — Default ist SeaBIOS. Config nach dem Restore manuell anpassen, falls deine Appliance OVMF braucht.
  • MAC-Adressen werden nicht übernommen (Proxmox vergibt beim Import neue).

Lokale Entwicklung (ohne Docker)

Du brauchst qemu-img, vma und zstd im PATH — also de facto einen Proxmox-Host. Dann:

pip install -e .
ova2vzdump convert appliance.ova ./out