qemu-compose
qemu-compose aims to provide a docker-compose style composer for qemu command, with advanced scripting feature as well as http support for cloud init or general purpose provisioning.
Bring up a qemu VM by providing a qemu-compose.yml and run qemu-compose up
Advantages
- Very simple and robust, written of several pure python scripts, depends on
qemucommands only. - No libvirt stuff, no daemon process, no xml config, no complex abstraction, just a simple wrapper around qemu args.
- support
before_scriptandafter_scriptfor setup and cleanup - support
boot_commandsfor vm provisioning (implemented using tty communication, gui not supported yet, and use jsonlisp for expressive power which apparently is turing-complete) - support
http_servefor cloudinit - env interpolation for advanced configuration
Installation
$ pip install qemu-compose
Examples and Screenshot
Example 1: pull archlinux docker image and run archlinux image directly with systemd init just like normal vm boot, with ssh login setup:
$ qemu-compose pull --boot systemd --kernel /boot/vmlinuz-linux --initrd /boot/initramfs-linux.img --disk-size 256G dockerproxy.net/library/archlinux:latest
$ qemu-compose run dockerproxy.net/library/archlinux:latest
Demo:
Example 2: download and bring up qemu vm with ubuntu cloudimg disk file, run a interactive shell
$ cd ./examples/ubuntu-cloudimg__amd64/
$ qemu-compose up
Demo:
Pull OCI/Docker Images
qemu-compose pull imports an OCI/Docker image into the local qemu-compose image store as a bootable qcow2 image.
The imported image is stored under:
~/.local/share/qemu-compose/image/<sha256>/
and can then be listed, tagged, removed, or run with the normal image commands:
$ qemu-compose images
$ qemu-compose run IMAGE[:TAG]
$ qemu-compose tag SOURCE_IMAGE TARGET_IMAGE
$ qemu-compose rmi IMAGE
Dependencies
The pull/import path uses external tools:
skopeo: pull/copy OCI images from registriesumoci: unpack OCI imagesqemu-img: create qcow2 disksguestfish: format and populate the qcow2 root filesystemopenssl: hash--root-passwordvalues
On Arch Linux:
$ sudo pacman -S --needed skopeo umoci qemu-img libguestfs openssl
Registry proxies are configured through the normal environment variables used by skopeo, for example:
$ HTTPS_PROXY=http://127.0.0.1:8123 qemu-compose pull ...
Basic Container Boot
Docker/OCI images are root filesystems, not full VM disks. They do not normally include a bootloader, kernel, or initramfs, so pull requires direct-boot assets:
--kernel: Linux kernel file copied into the qemu-compose image--initrd: initramfs file copied into the qemu-compose image
Example:
$ qemu-compose pull \
--kernel /boot/vmlinuz-linux \
--initrd /boot/initramfs-linux.img \
--disk-size 512M \
alpine:3.20
By default, --boot container is used. qemu-compose injects /qemu-compose-init into the rootfs and boots with:
init=/qemu-compose-init
That init script mounts basic pseudo-filesystems and runs the OCI image's Entrypoint/Cmd. This is useful for lightweight container-like VM images.
Systemd Boot
Use --boot systemd for images that contain systemd, such as Arch Linux:
$ qemu-compose pull \
--boot systemd \
--kernel /boot/vmlinuz-linux \
--initrd /boot/initramfs-linux.img \
--disk-size 2G \
dockerproxy.net/library/archlinux:latest
In systemd mode, qemu-compose prepares the rootfs for a normal serial-console VM boot:
- boots
init=/usr/lib/systemd/systemd - writes
/etc/fstabfor/dev/vda1 - clears
/etc/machine-idso systemd regenerates it - enables
serial-getty@ttyS0.servicewhen available - writes a DHCP
systemd-networkdprofile and enablessystemd-networkdwhen available - enables
systemd-resolved,sshd, andqemu-guest-agentwhen those units exist - masks
systemd-imds-generatorwhen present to avoid noisy minimal-image boot warnings
This mode makes an OCI Arch Linux rootfs boot to a normal systemd multi-user login prompt, but it does not install missing packages. If the image does not contain systemd or SSH packages, pull will not add them.
Root Login
By default, pull unlocks root with an empty password so serial console login works immediately:
archlinux login: root
Password:
Press Enter at the password prompt.
To set a real root password instead:
$ qemu-compose pull \
--boot systemd \
--root-password testpass \
--kernel /boot/vmlinuz-linux \
--initrd /boot/initramfs-linux.img \
dockerproxy.net/library/archlinux:latest
--empty-root-password is also accepted explicitly, but it cannot be used together with --root-password.
Updating Existing Imports
The image directory name is based on the OCI image digest. If the digest is already present, use --force to rebuild it with different options:
$ qemu-compose pull --force --boot systemd ...
Use --keep-workdir to keep temporary import files when debugging a failed import.
Limitations
pullcurrently creates one ext4 root partition at/dev/vda1.- It uses direct kernel boot, not GRUB/UEFI boot inside the imported disk.
- Container boot mode runs the OCI command; systemd boot mode boots systemd, but does not install extra packages.
- Full devbox images with custom packages, dotfiles, certificates, GRUB, or cloud-init still require a provisioning/build layer on top of the imported rootfs.
SSH Helper
qemu-compose provides a helper to invoke ssh with the instance key and safe defaults.
- Usage:
qemu-compose ssh [OPTIONS] VMID COMMAND [ARG...] - Defaults added by qemu-compose:
-o StrictHostKeyChecking=no-o UserKnownHostsFile=/dev/null-i ~/.local/share/qemu-compose/instance/VMID/ssh-key- appends
root@vsock%<cid>as the default destination (falls back toroot@vsock%${cid}if CID is unknown)
Examples:
# Print the default ssh command that would be used for a given VMID
$ qemu-compose ssh <vmid>
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i ~/.local/share/qemu-compose/instance/<vmid>/ssh-key root@vsock%<cid>
# Connect using vsock destination (cid recorded when the VM booted)
$ qemu-compose ssh <vmid> root@vsock%<cid>
# Connect over TCP instead (override destination and add your own options)
$ qemu-compose ssh <vmid> -p 2222 root@127.0.0.1
# Run a remote command
$ qemu-compose ssh <vmid> root@vsock%<cid> uname -a
Notes:
- The instance key is generated at first boot and stored under
~/.local/share/qemu-compose/instance/<vmid>/ssh-key. - Any ssh options you pass will be forwarded; the last-specified option wins.
Down Command
Stop and remove a VM instance.
$ qemu-compose down [identifier] [options]
Options:
identifier: Instance ID, unique prefix, or assigned name (optional if config file exists)-f, --file FILE: Compose configuration file to parse for instance name--force: Force removal without confirmation
Examples:
# Stop and remove instance by name
$ qemu-compose down my-vm
# Use config file to auto-detect instance name
$ cd ./examples/ubuntu-cloudimg__amd64/
$ qemu-compose down
# Use specified config file
$ qemu-compose down -f /path/to/qemu-compose.yml
# Force removal
$ qemu-compose down --force my-vm
Note: If no identifier is provided, qemu-compose will look for qemu-compose.yml or qemu-compose.yaml in the current directory and use the name field as the instance identifier.
Tag Command
Create a tag that refers to an image, similar to docker tag.
$ qemu-compose tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]
SOURCE_IMAGE[:TAG]: Source image identifier (can be image ID, name, or name:tag)TARGET_IMAGE[:TAG]: Target image name and optional tag (defaults tolatestif not specified)
Behavior:
- If the target tag already exists on another image, it will be moved to the source image
- Tags are stored in the image's
manifest.jsonfile under therepo_tagsfield
Examples:
# Tag an image by ID
$ qemu-compose tag 94a0434d0f73 devbox:archlinux
# Tag an image by name
$ qemu-compose tag my-image:v1 my-image:latest
# Create a new tag for an existing image
$ qemu-compose tag ubuntu:20.04 ubuntu:focal
# Replace an existing tag (moves tag from one image to another)
$ qemu-compose tag new-image:v1 existing-tag:latest
Notes:
- Image IDs can be specified as full SHA256 digest or unique prefix
- When a tag already exists on a different image, the tag is moved (not copied) to the new image
- Use
qemu-compose imagesto list all images and their tags
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file qemu_compose-1.1.1.tar.gz.
File metadata
- Download URL: qemu_compose-1.1.1.tar.gz
- Upload date:
- Size: 122.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a09afcd8527041df7adf71eb456791666ba68322bc60e21992f84dda03bfc7fd
|
|
| MD5 |
9f25697ef63fe1ae78220c906a938ca5
|
|
| BLAKE2b-256 |
8408090ced234680e28d438bc6b9797356d727f3785747ec328c512e07dfc1a6
|
Provenance
The following attestation bundles were made for qemu_compose-1.1.1.tar.gz:
Publisher:
pypi.yml on zTrix/qemu-compose
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
qemu_compose-1.1.1.tar.gz -
Subject digest:
a09afcd8527041df7adf71eb456791666ba68322bc60e21992f84dda03bfc7fd - Sigstore transparency entry: 2711281695
- Sigstore integration time:
-
Permalink:
zTrix/qemu-compose@f907b7ba917ab3efd1f4e9ee5258e7e3b9c18c77 -
Branch / Tag:
refs/tags/v1.1.1 - Owner: https://github.com/zTrix
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi.yml@f907b7ba917ab3efd1f4e9ee5258e7e3b9c18c77 -
Trigger Event:
release
-
Statement type:
File details
Details for the file qemu_compose-1.1.1-py3-none-any.whl.
File metadata
- Download URL: qemu_compose-1.1.1-py3-none-any.whl
- Upload date:
- Size: 151.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2883d7adb6aa39b6f2817879aee261ffa331e7c6cde8ced4efccf6008c33931c
|
|
| MD5 |
df65fbf97ae1b14f627b7a3a543e359c
|
|
| BLAKE2b-256 |
786e98846fcb3dd959f6d14f36acd80aa84cba1be577463d1f405c2d42838c3c
|
Provenance
The following attestation bundles were made for qemu_compose-1.1.1-py3-none-any.whl:
Publisher:
pypi.yml on zTrix/qemu-compose
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
qemu_compose-1.1.1-py3-none-any.whl -
Subject digest:
2883d7adb6aa39b6f2817879aee261ffa331e7c6cde8ced4efccf6008c33931c - Sigstore transparency entry: 2711282622
- Sigstore integration time:
-
Permalink:
zTrix/qemu-compose@f907b7ba917ab3efd1f4e9ee5258e7e3b9c18c77 -
Branch / Tag:
refs/tags/v1.1.1 - Owner: https://github.com/zTrix
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi.yml@f907b7ba917ab3efd1f4e9ee5258e7e3b9c18c77 -
Trigger Event:
release
-
Statement type: