virtual machine
You’re setting up a virtual machine on Linux and need it to work reliably — not just boot, but perform, scale, and integrate cleanly with your existing tooling.
This guide walks you through deploying a production-grade KVM-based virtual machine on a modern Linux host, using `virt-install`, managing it with `virsh`, and verifying its integration into your system. It’s for sysadmins, DevOps engineers, and developers who need full OS isolation — not containers, not emulation, but real virtualization with minimal overhead.
You’ll use KVM (Kernel-based Virtual Machine), the standard virtualization solution on Linux since kernel 2.6.20. We test on Ubuntu 22.04 LTS with QEMU 7.2.0, libvirt 8.6.0, and OpenSSH 8.9p1. If you’re on RHEL 9, Debian 12, or another recent distribution, the commands remain valid with minor path differences.
---
Requirements
You need:- A 64-bit x86 CPU with hardware virtualization (Intel VT-x or AMD-V). Verify with:
- At least 4 GB free RAM (8 GB recommended).
- 20 GB free disk space for the VM image.
- Root or sudo access.
- The `qemu-kvm`, `libvirt-daemon-system`, `libvirt-clients`, and `virtinst` packages installed.
On RHEL/CentOS/Fedora: ```bash sudo dnf install -y @virtualization sudo systemctl enable --now libvirtd ```
The `libvirtd` service must be running. Check: ```bash sudo systemctl is-active libvirtd ``` If inactive, start it: ```bash sudo systemctl start libvirtd ```
You’re now ready to create a VM.
---
Steps
1. Prepare the installation mediaDownload an ISO image you’ll use to install the guest OS. For this example, use Ubuntu Server 22.04.3 LTS: ```bash wget https://releases.ubuntu.com/22.04.3/ubuntu-22.04.3-live-server-amd64.iso -O ~/ubuntu-server.iso ```
Store it outside `/tmp` — reboots or cleanup scripts may delete it. Keep it in your home directory or a dedicated `/var/lib/libvirt/isos/` folder.
This ISO is your installation source. Without it, `virt-install` can’t boot the VM into installer mode.
2. Create a storage pool (optional but recommended)
By default, libvirt stores VM disks in `/var/lib/libvirt/images/`. You can keep it there, but managing multiple VMs becomes messy.
Create a dedicated storage pool: ```bash sudo mkdir -p /var/lib/libvirt/storage sudo virsh pool-define-as default dir --target /var/lib/libvirt/storage sudo virsh pool-start default sudo virsh pool-autostart default ```
This defines a directory-based storage pool named `default`. Now all new volumes will go into `/var/lib/libvirt/storage`.
You could use LVM, NFS, or iSCSI instead — but directory pools are simpler and sufficient for most use cases. The trade-off? Less performance than block storage, but easier to back up and inspect.
3. Allocate disk space for the VM
Create a 20 GB qcow2 disk image: ```bash sudo qemu-img create -f qcow2 /var/lib/libvirt/storage/webserver01.qcow2 20G ```
The `qcow2` format supports snapshots, compression, and sparse allocation (only uses disk space when data is written). It’s slower than raw, but far more flexible.
If you need maximum I/O performance (e.g., for a database VM), use raw: ```bash sudo qemu-img create -f raw /var/lib/libvirt/storage/webserver01.raw 20G ```
But you lose snapshots and thin provisioning. Choose based on workload.
4. Create the virtual machine with virt-install
Run: ```bash sudo virt-install \ --name webserver01 \ --memory 2048 \ --vcpus 2 \ --os-variant ubuntu22.04 \ --disk path=/var/lib/libvirt/storage/webserver01.qcow2,format=qcow2,bus=virtio \ --network bridge=virbr0,model=virtio \ --graphics vnc,listen=0.0.0.0 \ --console pty,target_type=serial \ --cdrom ~/ubuntu-server.iso \ --boot uefi \ --noautoconsole ```
Let’s break this down:
- `--name`: Sets the VM identifier. Used in `virsh` commands.
- `--memory`: Allocates 2 GB RAM. Below 1 GB may fail Ubuntu Server install.
- `--vcpus`: Assigns 2 virtual CPUs. Match to available host cores.
- `--os-variant`: Helps libvirt apply optimal settings (e.g., disk bus, firmware). Run `osinfo-query os` to list valid values.
- `--disk`: Attaches the image. `bus=virtio` enables paravirtualized drivers — 30–50% better I/O performance than IDE or SATA.
- `--network`: Connects to NAT bridge `virbr0` (created by default). `model=virtio` again for speed.
- `--graphics`: Enables VNC server. Accessible via `virt-viewer` or `remote-viewer`.
- `--console`: Allows serial console access over SSH or `virsh console`.
- `--cdrom`: Boots from ISO. After first boot, remove this flag.
- `--boot uefi`: Uses UEFI firmware. Required for Secure Boot or newer guest OSes.
- `--noautoconsole`: Prevents automatic GUI popup. Useful on headless servers.
5. Connect to the installer using virt-viewer
Install `virt-viewer` if not present: ```bash sudo apt install -y virt-viewer ```
Then connect: ```bash remote-viewer vnc://127.0.0.1:$(sudo virsh vncdisplay webserver01) ```
This extracts the VNC port (e.g., `:0` → port 5900) and opens a window with the installer.
If you’re on a remote server without GUI, use SSH tunneling: ```bash ssh -L 5900:127.0.0.1:5900 user@your-host ``` Then open `vnc://localhost:5900` locally.
You now see the Ubuntu installer. Proceed with installation — partitioning, user setup, package selection.
During install, ensure you:
- Select OpenSSH server.
- Use the same hostname (`webserver01`) for consistency.
- Avoid full disk encryption unless required — it complicates remote reboots.
After install completes, the VM tries to reboot — but still sees the ISO as first boot device. It will boot back into installer.
Fix this: ```bash sudo virsh change-media webserver01 hda --eject --config ```
This ejects the ISO from the virtual CD-ROM. The `--config` flag makes it persistent across reboots.
Now reboot the VM: ```bash sudo virsh reboot webserver01 ```
It should now boot from the virtual disk.
If it doesn’t, check boot order: ```bash sudo virsh dumpxml webserver01 | grep -A5 -B5 'boot ' ```
Ensure `
Force disk boot once: ```bash sudo virsh destroy webserver01 sudo virsh start webserver01 --boot hd ```
7. Enable autostart (optional)
Make the VM start automatically with the host: ```bash sudo virsh autostart webserver01 ```
Useful for production services. But be cautious: if the host boots with full CPU/RAM load from multiple VMs, performance suffers during startup.
Disable if you prefer manual control: ```bash sudo virsh autostart --disable webserver01 ```
8. Secure remote access via SSH
Once the guest OS boots, get its IP: ```bash sudo virsh domifaddr webserver01 ```
Look for the IPv4 address under `vnet0`. Example output: ``` Name MAC address Protocol Address vnet0 52:54:00:12:34:56 ipv4 192.168.122.10/24 ```
Connect via SSH: ```bash ssh user@192.168.122.10 ```
From outside the host, you’ll need port forwarding or a public bridge. NAT (`virbr0`) only allows inbound connections if you set up SSH forwarding.
Add a rule: ```bash sudo iptables -t nat -A PREROUTING -p tcp --dport 2222 -j DNAT --to-destination 192.168.122.10:22 sudo iptables -A FORWARD -p tcp -d 192.168.122.10 --dport 22 -j ACCEPT ```
Now `ssh -p 2222 user@host-public-ip` reaches the VM.
Better long-term: replace NAT with a bridged network using a physical interface. More complex, but gives VMs public IPs directly.
---
Verification
Confirm the VM is running and responsive: ```bash sudo virsh list --state-running ```Should show `webserver01`.
Check resource usage: ```bash sudo virsh dominfo webserver01 ```
Verify CPU, memory, and uptime match your configuration.
Test console access: ```bash sudo virsh console webserver01 ```
Press Enter. You should see a login prompt. Type `exit` to close.
Ensure it survives reboot: ```bash sudo virsh reboot webserver01 sleep 30 sudo virsh domifaddr webserver01 ```
The IP should reappear within 30 seconds.
If all checks pass, the VM is operational.
---
Frequently Asked Problems
1. “Error: unsupported configuration: CPU mode ‘host-passthrough’ for x86_64 qemu uri requires QEMU to be running as root”This happens when using `virt-manager` or custom XML with advanced CPU settings. Fix: run libvirtd as root (default), or switch to `host-model`:
```bash
sudo virsh edit webserver01
```
Change:
```xml
`host-model` provides most performance benefits without requiring root QEMU processes.
2. VM gets no IP address via `domifaddr`
`virsh domifaddr` relies on DHCP and guest ARP announcements. If the guest uses static IP or has slow networking, it won’t appear.
Alternative: log in via `virsh console` and run: ```bash ip a ```
Or install `libguestfs-tools` and inspect offline: ```bash sudo virt-cat -d webserver01 /etc/netplan/00-installer-config.yaml ```
3. VNC connection refused or black screen
Check if VNC is enabled: ```bash sudo virsh dumpxml webserver01 | grep vnc ```
Should show `
Ensure the firewall allows the port: ```bash sudo ufw allow 5900 ```
Or disable firewall temporarily: ```bash sudo ufw disable ```
Black screen after boot? The guest may use Wayland or have GPU issues. Use `virsh console` instead.
Virtual machines isolate workloads at the OS level, unlike containers — each runs its own kernel, init system, and drivers.
That isolation comes at a cost: higher memory overhead, slower boot times, and more complex networking than containers.
But when you need to run different kernels, test hypervisor features, or host legacy applications, VMs are still the only option.
You could use Docker or Podman for lighter workloads — and you probably should, if the app allows it.
But when the guest must be Windows, or run kernel modules, or simulate bare metal, you don’t get to choose.
KVM gives you enterprise-grade virtualization without licensing fees — provided you’re willing to manage it yourself.
Zelf hosten klinkt goed totdat je midden in de nacht wakker wordt omdat een VM geen IP kreeg na een host reboot. Dan realiseer je je: automatisering is geen luxe. Het is de reden dat je überhaupt virtuele machines gebruikt.