Run other operating systems
Boot a Linux distribution, a BSD, or other x86-64 OS images as a QEMU/KVM guest inside your container.
Run full, hardware-accelerated virtual machines inside your container - your own kernel, your own operating system, on your own bare metal.
Hoody containers are fast and lightweight, but sometimes you need a full virtual machine: a different kernel, a different operating system, or a workload that expects real VM hardware. On a dedicated server you rent, you can enable KVM passthrough and run QEMU/KVM virtual machines directly inside your container, using the host CPU’s hardware virtualization extensions.
Run other operating systems
Boot a Linux distribution, a BSD, or other x86-64 OS images as a QEMU/KVM guest inside your container.
Virtualization-based workloads
Run workloads that expect a real VM - test kernels and kernel modules, or run VM-based CI.
A different kernel
Need a kernel version or configuration the host does not provide? Boot your own inside a VM.
Hardware-accelerated
KVM uses the CPU’s VT-x / AMD-V extensions, so guests run at near-native CPU performance - not slow emulation.
Use a dedicated (bare-metal) server you rent. Free-tier and subserver containers cannot enable KVM (the request is refused with a 403). The server must also carry a memory and CPU size; an unsized server is rejected. If you do not have one yet, rent a server. kvm_supported: true on the server tells you it is ready - see below.
Enable kvm for the container. It is a simple true/false setting, off by default. The canonical name is kvm; dev_kvm is accepted as an alias.
You can turn it on at creation - pass kvm: true in the create request (or --kvm on the CLI) - or later, on a stopped container, with the dedicated command:
# Enable /dev/kvm on a stopped container (`hoody containers kvm disable` turns it back off)hoody containers kvm enable -c $CONTAINER_IDimport { HoodyClient } from 'hoody-sdk';const client = new HoodyClient({ baseURL: 'https://api.hoody.com', token: process.env.HOODY_TOKEN });
const result = await client.api.containers.enableKvm(CONTAINER_ID, {});console.log(result.data.kvm); // truecurl -X PUT https://api.hoody.com/api/v1/containers/$CONTAINER_ID/kvm \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"kvm": true}'The container must be stopped to change this setting - the toggle is rejected on any container that is not fully stopped, including one still being created.
Read the create response instead of predicting it. kvm: true at creation does not always come back enabled, even on a dedicated server. Three fields carry the outcome: kvm, kvm_note, and the container’s status.
| Response | What it means |
|---|---|
kvm: true | KVM was enabled during the create. Confirm it with a fresh read (below): a concurrent disable can make it false, and the reported true is not always confirmed. |
kvm: false with a kvm_note, on a dedicated (bare-metal) server | KVM could not be enabled during this create. Enable it with the toggle once the container is stopped. |
kvm: false with a note saying the container has no placement to host KVM | The create had no server placement. |
kvm: false with a note telling you to start the container and enable KVM with the toggle | Enabling KVM failed partway. The note reports the container as stopped, but that may not be true yet, so read the container’s current status rather than the note. |
| A note telling you to start the container yourself | KVM was enabled but the container was not started again. kvm can be false or an unconfirmed true, and in that last case this note is the only one the response carries. |
Start the container. The /dev/kvm grant is applied when the container starts.
Verify /dev/kvm is present inside the container:
ls -l /dev/kvm# crw-rw-rw- 1 root root ... /dev/kvm - present, world-readable/writable (mode 0666)The node is world-readable/writable (mode 0666), so you can run QEMU as a non-root user inside the container.
To turn it back off, stop the container, set kvm to false (same endpoint), and start it again. That removes the /dev/kvm device and leaves the container in a KVM-off state: the VM runtime packages, your disk images and anything else a guest wrote are still on disk, and nothing but the grant is taken away. Turning KVM on again restores the device.
When you copy a container, KVM is a fresh decision for the copy - the grant never transfers from the source. A copy of a KVM container starts KVM-off unless you ask for it, and a copy of an ordinary container can be granted KVM on the spot. This keeps the copy’s capability tied to its own server, not the original’s.
To grant the copy /dev/kvm, pass kvm: true on the copy request:
# Copy a container and grant the copy its own /dev/kvmhoody containers copy $CONTAINER_ID --target-project-id $PROJECT_ID --kvmconst copy = await client.api.containers.copy(CONTAINER_ID, { target_project_id: PROJECT_ID, kvm: true});console.log(copy.data.kvm, copy.data.kvm_note);curl -X POST https://api.hoody.com/api/v1/containers/$CONTAINER_ID/copy \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"target_project_id": "'"$PROJECT_ID"'", "kvm": true}'The same rules apply to the copy as to any other container: the target must be a dedicated (bare-metal) server (a free-tier or subserver target is refused with a 403), it must be sized (an unsized target is a 400 rather than a 403), and a scoped token needs the containers.features.kvm permission.
On a dedicated target the copy comes back kvm: false with a kvm_note, and you enable it with the toggle once the copy is stopped.
With /dev/kvm available, any KVM-based tool works. Start by checking that the runtime is in this container:
command -v qemu-system-x86_64 # the hypervisor, from qemu-system-x86command -v swtpm_setup # manufactures a vTPM, from swtpm-toolsqemu-system-x86_64 -display help | grep -x sdl # SDL display backend, from qemu-system-guiThe first two lines print a path, or nothing if that package is missing. The third prints sdl only if QEMU can load its SDL display module, which the boot command below uses; the hypervisor can be installed without it, and -display sdl then fails. It is the same probe the installer runs.
The VM runtime rides the Hoody Kit. A container created with the kit (the default) runs the installer, which puts qemu-system-x86 and qemu-system-gui, which carries the SDL display module, qemu-utils, ovmf for UEFI firmware including the Secure Boot pair, passt for unprivileged guest networking, virtiofsd for file sharing, swtpm and swtpm-tools for a virtual TPM, osinfo-db for OS metadata, and curl in place. The installer targets Debian 13 or later on amd64 and skips anything else, and a container created with hoody_kit: false never runs it at all. So the kit is not a promise by itself and its absence is not a verdict either: a container you installed QEMU into yourself has it. The three checks above cover the hypervisor, vTPM manufacturing, and the SDL display that the boot command below uses. They do not probe the rest of the set.
If any check prints nothing, install the set yourself:
sudo apt-get update && sudo apt-get install -y qemu-system-x86 qemu-system-gui \ qemu-utils ovmf passt virtiofsd swtpm swtpm-tools osinfo-db curlThen boot a guest:
# -display sdl draws the guest window on the X display in $DISPLAYqemu-system-x86_64 -enable-kvm -m 2048 -display sdl -cdrom your-os.isoThe runtime and the grant are two separate things, and they arrive by different routes: the packages
ride the Hoody Kit, while /dev/kvm is the per-container grant you enabled above. A container with
the packages but no grant simply has no /dev/kvm to accelerate against.
If -enable-kvm is accepted, you are running a hardware-accelerated virtual machine inside your container.
QEMU draws the guest on an X display, so start it from a session that has one. A terminal opened in the browser at terminal-N exports DISPLAY=:N into its shell, and display N is served at its own URL:
https://{projectId}-{containerId}-display-{N}.{serverName}.containers.hoody.comOpen that URL and the QEMU window is on it. Two kinds of session have no display of their own: one created through the terminal API without a display field in the request body, and an SSH session. In either, export DISPLAY=:N yourself to target a display that is already running. Displays covers how instances and URLs line up.
Every container read carries its server’s KVM facts in the server object, and every read of your current rental carries them on the rented server. You need only the usual read access (containers.read for containers); nothing here changes anything.
| Field | Meaning |
|---|---|
kvm_supported | true: this server can give a container /dev/kvm. false: it cannot (free tier, subserver, server not rented, or the host is not set up for KVM). null: not known right now. |
kvm_nested | true: VMs you run here may themselves run hardware-accelerated VMs (nested virtualization). false: they may not. null: not known right now. Always false when kvm_supported is false. |
kvm_checked_at | When Hoody last checked the host. null if it never has. |
If the last check is more than an hour old, kvm_supported and kvm_nested are null (with kvm_checked_at still telling you when the last check was), so a true or false is always recent. Past rentals of a server you no longer hold show null for all three.
kvm_supported describes the server, not your request: it says nothing about whether the container is stopped (required to change kvm), whether your token has the KVM permissions, or whether the container already has KVM - that is the container’s own kvm field.
hoody containers get $CONTAINER_ID -o json | jq '.server | {kvm_supported, kvm_nested, kvm_checked_at}'const { data } = await client.api.containers.get(CONTAINER_ID);console.log(data.server?.kvm_supported, data.server?.kvm_nested, data.server?.kvm_checked_at);curl https://api.hoody.com/api/v1/containers/$CONTAINER_ID \ -H "Authorization: Bearer $TOKEN" | jq '.data.server | {kvm_supported, kvm_nested, kvm_checked_at}'
# Your rented servers carry the same three fieldscurl https://api.hoody.com/api/v1/rentals -H "Authorization: Bearer $TOKEN"With KVM you run virtual machines inside your container. Nested virtualization goes one level further: a VM you start may itself use hardware virtualization to run its own VMs (for example a hypervisor, Android emulator or VM-based CI running inside your guest).
Nested virtualization is a setting of the whole physical server, and it is off by default on every Hoody server: it adds a large, complex layer to the hypervisor, so it is enabled only on request. Ordinary VMs work the same either way - kvm_nested: false only means your guest sees no virtualization extensions of its own.
If you need it, contact Hoody support for your dedicated server. Once kvm_nested reads true, start the guest with the host CPU model so the guest sees the extensions:
qemu-system-x86_64 -enable-kvm -cpu host -m 4096 -display sdl -drive file=guest.qcow2# inside the guest: grep -cE 'vmx|svm' /proc/cpuinfo -> non-zeroA change takes effect right away only on a server where nothing holds KVM: no container, running or stopped, and no snapshot with KVM enabled. Otherwise it takes effect after a later restart of the server. kvm_nested always shows the server’s actual state, not the request, so read it again before relying on it.
For more on how Hoody isolates workloads, see platform security. To get a server that supports KVM, see renting servers.