A Full-Fledged Virtual Machine Inside Docker with a Web Interface
Sometimes you need to quickly check a questionable script, test software installation on a clean distribution, or deploy an environment with a specific kernel. Firing up VirtualBox for this is too much hassle, setting up Proxmox on a work laptop is overkill, and a regular Docker container doesn't provide kernel-level isolation.
Recently I stumbled upon a repository qemus/qemu. The project packages QEMU with KVM acceleration inside a regular Docker container and exposes VM management directly in the browser.
How the Project Works
Essentially, this is a wrapper around QEMU that handles all the tedious setup of the emulator, networking, disks, and noVNC client. You don't need to manually write long command-line flags or mess with XML configs libvirt.
All management boils down to passing environment variables to docker-compose.yml. The image automatically downloads the required Linux distribution, creates a virtual disk, connects KVM for near-native performance, and opens the web interface on port 8006.
The container is suitable for both local development and running VMs in Kubernetes or GitHub Codespaces.
Quick Start
To launch it, you only need a minimal compose file. For example, let's deploy Linux Mint:
services:
qemu:
image: qemux/qemu
container_name: qemu
environment:
BOOT: "mint"
RAM_SIZE: "4G"
CPU_CORES: "2"
devices:
- /dev/kvm
- /dev/net/tun
cap_add:
- NET_ADMIN
ports:
- 8006:8006
volumes:
- ./qemu_data:/storage
restart: always
stop_grace_period: 2m
After running docker compose up -d, the service automatically downloads the Mint ISO and starts virtualization.
Navigate to http://localhost:8006 in your browser and you'll see the OS installation screen. No additional VNC clients need to be installed.
# Если вы предпочитаете чистый Docker CLI:
docker run -it --rm --name qemu \
-e "BOOT=alpine" \
-p 8006:8006 \
--device=/dev/kvm \
--device=/dev/net/tun \
--cap-add NET_ADMIN \
-v "${PWD}/qemu_data:/storage" \
--stop-timeout 120 \
docker.io/qemux/qemu
What the Container Can Do
Automatic Distribution Downloads
In the BOOT variable, you can specify a short name of a popular distribution. The script knows where to fetch the latest images:
arch,debian,fedora,ubuntu,alpinekali,nixos,gentoo,mint,manjarorocky,alma,cachy,tails
If the system you need isn't in the preset list, you can pass a direct link to any image:
environment:
BOOT: "https://dl-cdn.alpinelinux.org/alpine/v3.19/releases/x86_64/alpine-virt-3.19.1-x86_64.iso"
The container unpacks archives on the fly and supports images in .iso, .qcow2, .vmdk, .vhdx, and .vdi formats. You can also mount a local file directly into the container root as /boot.iso.
Working with Disks and Memory
By default, the VM gets a 64 GB disk in qcow2 format inside the /storage directory. You can change the size at any time via the DISK_SIZE: "128G" variable.
If the host runs out of RAM, the container supports memory ballooning. This helps dynamically return unused guest memory to the host.
File Sharing with the Host
To transfer files between the host and VM, the 9pfs protocol is used:
volumes:
- ./my_host_folder:/shared
Inside the running guest system, you just need to mount the directory:
mount -t 9p -o trans=virtio shared /mnt/shared
Port Forwarding and Full Networking
With the standard Docker bridge mode, port forwarding is configured via the familiar ports block. To forward SSH externally:
ports:
- 8006:8006
- 2222:22
If the VM needs its own IP address on the local network (so the home router assigns it a separate address via DHCP), the container can be connected via macvlan. In this case, there's no need for constant port mapping.
Passing Through Real Hardware
Through the compose file, you can pass through real physical drives (/dev/sdb:/disk1) or USB devices by passing their identifiers to QEMU:
environment:
ARGUMENTS: "-device usb-host,vendorid=0x1234,productid=0x5678"
devices:
- /dev/bus/usb
System Requirements and Limitations
The main caveat lies in KVM support.
Without direct access to the /dev/kvm device, hardware acceleration won't work, and pure software CPU emulation will make performance unbearably slow.
Where it works smoothly:
- Linux hosts with hardware virtualization support (Intel VT-x / AMD-V).
- Windows 11 with Docker Desktop or Podman Desktop with nested virtualization enabled.
Docker Desktop on macOS and Windows 10 currently doesn't pass /dev/kvm into containers, so you won't be able to run the project there.
You can check KVM availability on Linux with the standard utility:
sudo apt install cpu-checker
sudo kvm-ok
If the command outputs KVM acceleration can be used, the container will start without issues.
What You Can Use This For
- Testing automation scenarios. Running clean Ansible or bash script installations in an isolated VM without risking breaking your working system.
- Safe software execution. Checking suspicious packages or binaries in an isolated environment that can be recreated with a single command.
- Learning and experiments. Exploring new distributions (e.g., NixOS or Alpine) right in the browser window without dealing with USB drives.
- CI/CD pipelines. Building kernel packages or testing system software inside full virtual machines on Kubernetes runners.
Summary
The qemus/qemu project perfectly fills the niche between lightweight containers and heavy hypervisors. If you regularly need isolated virtual machines for testing on a Linux machine or home server, this image saves a lot of time on image preparation and network configuration.
The repository is actively maintained by the qemus organization, which also has separate adapted builds for ARM64, Windows, and macOS.
Related projects