Skip to content

Create a Local VM on Linux (QEMU/libvirt)

On a Linux host you can run a meltcloud Nest or Machine as a local VM using QEMU/KVM, managed with libvirt.

VM Settings

SettingNestMachine
Architecturehost arch (amd64/arm64)host arch (amd64/arm64)
vCPU4+2+
RAM10 GiB+4 GiB+
Disk≥ 200 GiB≥ 30 GiB (depends on the amount of container images)
NetworkingDefault (NAT)Default (NAT)
Nested virtualizationnot requiredoptional, required for Elastic Node Pools
Boot mediaNest installer .isoEnrollment Image .iso

Prerequisites

Install packages

Install libvirt and qemu-system-x86 for your distro. The following installs it on Ubuntu 24.04+:

shell
sudo apt install qemu-system-x86 libvirt-daemon-system ovmf

Add your user to the libvirt group so you can manage VMs without root:

shell
sudo usermod -aG libvirt $USER

Log out and back in for the group change to take effect.

Start the default network

libvirt ships a default NAT network that provides DHCP and internet access. Make sure it is active:

shell
sudo virsh net-start default
sudo virsh net-autostart default

Copy the ISO

Create a directory for VM images and copy your .iso into it:

shell
# placed under /var/lib/libvirt so libvirt can traverse into it
sudo mkdir -p /var/lib/libvirt/melt-vms
sudo chown $USER:kvm /var/lib/libvirt/melt-vms

Copy the ISO you need: the Nest installer if you're setting up a Nest, or the Enrollment Image if you're enrolling a machine:

shell
cp /path/to/nest-installer-v*-amd64.iso /var/lib/libvirt/melt-vms/
shell
cp /path/to/enrollment-image-amd64.iso /var/lib/libvirt/melt-vms/

Create the VM

shell
VMDIR=/var/lib/libvirt/melt-vms
ISO_PATH=$VMDIR/nest-installer-v*-amd64.iso

# create the disk (adjust size as needed, minimum 200 GiB; qemu's G suffix = GiB)
qemu-img create -f qcow2 $VMDIR/melt-nest.qcow2 200G

# write the VM definition
cat > $VMDIR/melt-nest.xml << EOF
<domain type='kvm'>
  <name>melt-nest</name>
  <!-- RAM: 10 GiB minimum -->
  <memory unit='GiB'>10</memory>
  <!-- CPU: 4+ vCPUs recommended -->
  <vcpu>4</vcpu>
  <os firmware='efi'>
    <type arch='x86_64'>hvm</type>
    <boot dev='hd'/>
    <boot dev='cdrom'/>
  </os>
  <features>
    <acpi/>
  </features>
  <cpu mode='host-passthrough'/>
  <devices>
    <disk type='file' device='disk'>
      <driver name='qemu' type='qcow2'/>
      <!-- adjust path if you changed the qemu-img command above -->
      <source file='$VMDIR/melt-nest.qcow2'/>
      <target dev='vda' bus='virtio'/>
    </disk>
    <disk type='file' device='cdrom'>
      <source file='$ISO_PATH'/>
      <target dev='sda' bus='sata'/>
      <readonly/>
    </disk>
    <interface type='network'>
      <source network='default'/>
      <model type='virtio'/>
    </interface>
    <serial type='pty'>
      <target port='0'/>
    </serial>
    <console type='pty'>
      <target type='serial' port='0'/>
    </console>
  </devices>
</domain>
EOF

# define, start, and connect to the console
virsh define $VMDIR/melt-nest.xml
virsh start melt-nest --console
shell
VMDIR=/var/lib/libvirt/melt-vms
ISO_PATH=$VMDIR/enrollment-image-amd64.iso
# change the name to create multiple machines (e.g. worker1, worker2)
VM_NAME=worker1

# create the disk (adjust size as needed, minimum 30 GiB; qemu's G suffix = GiB)
qemu-img create -f qcow2 $VMDIR/$VM_NAME.qcow2 30G

# write the VM definition
cat > $VMDIR/$VM_NAME.xml << EOF
<domain type='kvm'>
  <name>$VM_NAME</name>
  <!-- RAM: 4 GiB minimum -->
  <memory unit='GiB'>8</memory>
  <!-- CPU: 2+ vCPUs -->
  <vcpu>2</vcpu>
  <os firmware='efi'>
    <type arch='x86_64'>hvm</type>
    <boot dev='hd'/>
    <boot dev='cdrom'/>
  </os>
  <features>
    <acpi/>
  </features>
  <cpu mode='host-passthrough'/>
  <devices>
    <disk type='file' device='disk'>
      <driver name='qemu' type='qcow2'/>
      <!-- adjust path if you changed the qemu-img command above -->
      <source file='$VMDIR/$VM_NAME.qcow2'/>
      <target dev='vda' bus='virtio'/>
    </disk>
    <disk type='file' device='cdrom'>
      <source file='$ISO_PATH'/>
      <target dev='sda' bus='sata'/>
      <readonly/>
    </disk>
    <interface type='network'>
      <source network='default'/>
      <model type='virtio'/>
    </interface>
    <serial type='pty'>
      <target port='0'/>
    </serial>
    <console type='pty'>
      <target type='serial' port='0'/>
    </console>
  </devices>
</domain>
EOF

# define, start, and connect to the console
virsh define $VMDIR/$VM_NAME.xml
virsh start $VM_NAME --console

Exit the console with Ctrl+5. To reconnect later:

shell
virsh console melt-nest

To clean up a VM:

shell
virsh destroy melt-nest
virsh undefine melt-nest --nvram

Next Steps

  • Nest: follow the on-screen TUI installer.
  • Machine: after it boots from the Enrollment Image it enrolls automatically and appears under Machines. Continue with Assign to a Machine Pool.

Troubleshoot

Nested virtualization not working

Nested virtualization is enabled by default on most recent Linux distributions. If it is not working, enable it manually:

shell
# Intel
echo "options kvm_intel nested=1" | sudo tee /etc/modprobe.d/kvm-nested.conf
sudo modprobe -r kvm_intel && sudo modprobe kvm_intel

# AMD
echo "options kvm_amd nested=1" | sudo tee /etc/modprobe.d/kvm-nested.conf
sudo modprobe -r kvm_amd && sudo modprobe kvm_amd

Verify with:

shell
cat /sys/module/kvm_intel/parameters/nested   # or kvm_amd

The VM must also use <cpu mode='host-passthrough'/> (already included in the XML definitions above).

Running on ARM (aarch64) hosts

This guide uses x86_64 examples, but ARM hosts work too. Adapt the following:

  • Install qemu-system-arm instead of qemu-system-x86, and qemu-efi-aarch64 instead of ovmf.
  • In the XML definitions, change <type arch='x86_64'>hvm</type> to <type arch='aarch64'>hvm</type>.
  • Use the arm64 variants of the installer and enrollment ISOs (e.g. nest-installer-v*-arm64.iso).

Other issues

If the VM has trouble booting or enrolling, check the Enrollment Images – Troubleshooting section.