# QEMU/KVM — Ubuntu VM deployment `deploy_qemu.py` deploys an Ubuntu VM (libvirt/KVM) from an official Ubuntu cloud image, using `qemu-img` + `cloud-init` + `virt-install`. It: 1. **Downloads the Ubuntu cloud image by itself** (cached, no double download). 2. Converts it to a dedicated qcow2 working disk and resizes it. 3. Generates `user-data` / `meta-data` and builds the `seed.iso` (cloud-init). 4. Runs `virt-install` importing the disk + the seed as a CD-ROM. 5. Waits for the DHCP lease and prints the SSH command. ## Prerequisites - A host with KVM available (bare-metal or nested virtualization enabled). - `sudo` rights (the deployment writes to `/var/lib/libvirt/images` and drives libvirt). ## Installation The script **auto-installs the missing pieces it needs**: on first run it detects your package manager (apt / dnf / pacman / zypper / brew), lists the missing components (the client tools, **plus the libvirt daemon and the QEMU system emulator**), asks for confirmation, installs them with `sudo`, then enables and starts `libvirtd`. Use `-y` to accept automatically or `--no-install-deps` to disable this behaviour. To install everything manually on Ubuntu/Debian (recommended full KVM stack): ```bash sudo apt install qemu-utils virtinst libvirt-clients cloud-image-utils \ libvirt-daemon-system qemu-system-x86 sudo systemctl enable --now libvirtd sudo usermod -aG libvirt,kvm "$USER" # re-login / reconnectez-vous ``` `libvirt-daemon-system` provides the `libvirtd` daemon (and the `/var/run/libvirt/libvirt-sock` socket) and `qemu-system-x86` the emulator — without them `virt-install` fails with *"Failed to connect socket to '/var/run/libvirt/libvirt-sock'"*. The script installs and starts them for you; this manual command is only needed if you prefer to prepare the host yourself or run with `--no-install-deps`. ## Usage Simplest form — the image is downloaded automatically (path derived from `--version`, cached in `/var/lib/libvirt/images/iso`): ```bash sudo ./script/qemu/deploy_qemu.py --name test-vm --version 24.04 \ --ssh-key ~/.ssh/id_ed25519.pub ``` Download (and verify) an image without creating a VM: ```bash sudo ./script/qemu/deploy_qemu.py --download-only --version 24.04 --verify ``` Deploy with an interactive password instead of an SSH key: ```bash sudo ./script/qemu/deploy_qemu.py --name test-vm --version 24.04 --ask-password ``` Larger VM (8 GB RAM, 8 vCPU, 120 GB disk), overwriting an existing disk: ```bash sudo ./script/qemu/deploy_qemu.py --name test-vm --version 24.04 \ --memory 8192 --vcpus 8 --disk-size 120G --ask-password --force ``` Preview what would happen, without doing anything (no sudo, no download): ```bash ./script/qemu/deploy_qemu.py --name test-vm --version 24.04 --dry-run ``` Non-interactive deployment (accept dependency install automatically): ```bash sudo ./script/qemu/deploy_qemu.py --name test-vm --version 24.04 \ --ssh-key ~/.ssh/id_ed25519.pub -y ``` Supported Ubuntu versions: `20.04`, `22.04`, `24.04` (default), `24.10`, `25.04`, `25.10`. Provide an explicit image path as a positional argument to override the automatic download location. ## After deployment ```bash virsh list --all virsh console test-vm # Ctrl+] to quit / pour quitter virsh domifaddr test-vm --source lease # find the IP / trouver l'IP ssh erplibre@ ``` The default user is `erplibre` (change it with `--user`). ## Via the TODO menu The script is integrated into the interactive assistant. Run `make todo` (or `./script/todo/todo.py`), then go to **Execute → Deploy → QEMU/KVM - Deploy an Ubuntu VM (libvirt)**. From there you can deploy a VM, preview a dry-run, download an image, list VMs and show a VM IP address — the menu asks for the parameters and builds the command for you. ## Main options - `--version` — Ubuntu version (default `24.04`). - `--image-dir` — image cache directory (default `/var/lib/libvirt/images/iso`). - `--download-only` — download the image then exit (no VM). - `--name` — VM name (required for deployment). - `--memory`, `--vcpus`, `--disk-size` — VM sizing. - `--ssh-key`, `--ask-password`, `--password-hash` — authentication. - `-y` / `--assume-yes` — auto-accept dependency installation. - `--no-install-deps` — never auto-install dependencies. - `--dry-run` — show the commands without executing anything. - `--force` — overwrite the existing working qcow2 disk. Run `./script/qemu/deploy_qemu.py --help` for the full list. ## Managing VMs List, stop and remove VMs (the qcow2 disk under `/var/lib/libvirt/images` is kept unless you delete it): ```bash sudo virsh list --all # toutes les VM et leur état / all VMs and state sudo virsh shutdown # arrêt propre ACPI / graceful shutdown sudo virsh destroy # arrêt forcé / force off (pull the plug) sudo virsh undefine # supprime la définition / remove definition sudo virsh domifaddr # adresse IP de la VM / VM IP address ``` `destroy` only powers the VM off (disk kept); `undefine` removes its definition. To fully recreate a VM with the same name, `destroy` + `undefine` it first, or redeploy with `--force`. ## SSH access from another machine (ProxyJump) With the default NAT network the VM is reachable **only from the KVM host**. To reach it from another machine **without changing the network**, use the host as a jump host (it already reaches the VM). Get the VM IP with `sudo virsh domifaddr `, then from the other machine: ```bash # Rebond SSH vers la VM / jump through the KVM host ssh -J user@ erplibre@ # Tunnel d'un service, ex. Odoo 8069 / tunnel a service, then http://localhost:8069 ssh -L 8069::8069 user@ ``` To make it permanent, add this to `~/.ssh/config` on the other machine (then just `ssh myvm`): ```text Host myvm HostName # ex. 192.168.122.50 (reseau NAT) User erplibre ProxyJump user@ # IP LAN de l'hote KVM ``` This works over Wi-Fi and needs no VM shutdown — the simplest option for personal access. Prefer a bridge (below) if the VM must be a full server exposed on the LAN. ## QEMU inside QEMU (nested) & exposing the VM via a bridge If the KVM host is **itself a VM** (QEMU-in-QEMU), the deployment works only when **nested virtualization** is enabled on the outer/physical host and the middle VM uses CPU mode `host-passthrough`. Check from inside the KVM host (the first command must be non-empty): ```bash grep -E -o '(vmx|svm)' /proc/cpuinfo | sort -u # extensions visibles / visible # Sur l'hote PHYSIQUE / on the PHYSICAL host: cat /sys/module/kvm_intel/parameters/nested # Intel -> Y/1 cat /sys/module/kvm_amd/parameters/nested # AMD -> Y/1 ``` To enable nesting on the physical host (Intel shown; use `kvm_amd` on AMD), then recreate the middle VM with `host-passthrough`: ```bash echo "options kvm_intel nested=1" | sudo tee /etc/modprobe.d/kvm-nested.conf sudo modprobe -r kvm_intel && sudo modprobe kvm_intel # ou / or reboot ``` If nesting is unavailable, QEMU still runs via software emulation (TCG) — it works but is slow. ### Bridge for external access A NAT VM is isolated; a **bridged** VM gets an IP directly on the LAN, reachable by any machine. On the KVM host, create a bridge `br0` over the physical NIC (**wired only** — Wi-Fi cannot be bridged). netplan (Ubuntu server) — replace `enp3s0` with your interface: ```yaml # /etc/netplan/01-br0.yaml network: version: 2 renderer: networkd ethernets: enp3s0: {dhcp4: no, dhcp6: no} bridges: br0: interfaces: [enp3s0] dhcp4: yes parameters: {stp: false, forward-delay: 0} ``` Apply safely (auto-reverts if you lose the connection) and verify — or use NetworkManager (Ubuntu desktop): ```bash # netplan sudo netplan try && sudo netplan apply ip addr show br0 # br0 porte l'IP du LAN / br0 holds the LAN IP # NetworkManager (alternative) nmcli con add type bridge ifname br0 con-name br0 nmcli con add type ethernet ifname enp3s0 master br0 con-name br0-port nmcli con modify br0 ipv4.method auto nmcli con down "Wired connection 1" ; nmcli con up br0 ``` Then attach the VM to the bridge — **either at creation**: ```bash sudo ./script/qemu/deploy_qemu.py --name --version 24.04 \ --ssh-key ~/.ssh/id_ed25519.pub --network bridge=br0,model=virtio -y --force ``` **or by editing a VM already created**: stop it, replace its `` block (`type='network'` / `` → `type='bridge'` / ``), then start it again: ```bash sudo virsh shutdown sudo virsh edit # mettre l'interface en bridge=br0 sudo virsh start sudo virsh domifaddr # nouvelle IP LAN / new LAN IP ``` The VM now gets a LAN IP from your router, reachable by other machines. From the Internet you additionally need a port-forward on your router (or a VPN); in a nested setup the outer host must also forward/expose the middle VM.