# Create KVM virtual machines with libvirt on a Linux VDS

> Run KVM guests inside a Linux VDS: install libvirt, start the default NAT network, create an Ubuntu VM from a cloud image with cloud-init and connect over SSH.

Difficulty: Advanced

A Linux VDS with nested virtualization can run **KVM** virtual machines of its own, managed with **libvirt**. That is useful for isolated test environments, CI machines or labs. This guide creates an Ubuntu guest from Ubuntu's official cloud image; cloud-init puts your SSH key into it, so there is no installer to click through. It builds on the check and package installation in [VDS and nested virtualization](/guides/vds-nested-virtualization).

## Before you start

- An Ubuntu or Debian **VDS**, nested dedicated or dedicated server where `kvm-ok` (package `cpu-checker`) prints `KVM acceleration can be used`.
- A user with `sudo` and an SSH key pair on your own computer; see [SSH keys](/guides/ssh-keys).
- Free disk space for the guests under `/var/lib/libvirt/images`.

## Step 1: Install the tools

```bash
sudo apt update
sudo apt install qemu-system-x86 qemu-utils libvirt-daemon-system virtinst
sudo usermod -aG libvirt $USER
```

Log out and in again so the group membership applies.

**Verify:** `virsh list --all` runs without a permission error.

## Step 2: Start the default network

libvirt's **default** network gives guests addresses in `192.168.122.0/24` and NAT to the internet:

```bash
virsh net-list --all
sudo virsh net-start default
sudo virsh net-autostart default
```

**Verify:** `virsh net-list` shows `default` as `active` with autostart `yes`.

## Step 3: Download the cloud image

Download Ubuntu 24.04's cloud image and its checksum list, and check the file:

```bash
cd /var/lib/libvirt/images
sudo wget https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img
sudo wget https://cloud-images.ubuntu.com/noble/current/SHA256SUMS
sha256sum -c SHA256SUMS --ignore-missing
```

**Verify:** the check prints `noble-server-cloudimg-amd64.img: OK`.

## Step 4: Create the guest's disk

Copy the image for the new guest and give it room to grow; cloud-init grows the file system on first boot:

```bash
sudo cp noble-server-cloudimg-amd64.img vm1.qcow2
sudo qemu-img resize vm1.qcow2 20G
```

## Step 5: Create and start the VM

Find the OS name libvirt knows for Ubuntu 24.04:

```bash
sudo apt install libosinfo-bin
osinfo-query os | grep -i ubuntu24
```

Create the guest, import the disk and pass your public SSH key to cloud-init. Copy your public key to the server first (for example to `~/id_ed25519.pub`):

```bash
virt-install --name vm1 --memory 2048 --vcpus 2 --disk /var/lib/libvirt/images/vm1.qcow2 --import --osinfo ubuntu24.04 --network network=default --cloud-init clouduser-ssh-key=$HOME/id_ed25519.pub --graphics none --noautoconsole
```

`--import` builds the VM around the existing disk instead of running an installer, and `clouduser-ssh-key` lets you log in as the image's default user, `ubuntu`.

**Verify:** `virsh list` shows `vm1` as `running`.

## Step 6: Connect to the guest

Wait a minute for the first boot, then look up the guest's address and connect from the server:

```bash
virsh domifaddr vm1
ssh ubuntu@192.168.122.50
```

From your own computer, jump through the server in one command:

```bash
ssh -J you@203.0.113.10 ubuntu@192.168.122.50
```

If SSH does not work yet, open the serial console with `virsh console vm1` and leave it with `Ctrl` + `]`.

## Step 7: Everyday management

```bash
virsh list --all
virsh shutdown vm1
virsh start vm1
virsh autostart vm1
```

`virsh autostart` starts the guest whenever the server boots. To delete a guest and its disk, run `virsh destroy vm1` and `virsh undefine vm1 --remove-all-storage`.

## Troubleshooting

**`virt-install` says KVM is not available or falls back to slow emulation.** The virtualization extensions are not visible; see [nested virtualization problems](/guides/nested-virtualization-problems).

**virt-install reports that an OS name is required, or does not know the name.** Use a name that `osinfo-query os` lists on your server; older systems may only know older Ubuntu releases.

**`virsh domifaddr` shows nothing.** The guest is still booting, or cloud-init has not configured the network yet. Wait, or check the boot output with `virsh console vm1`.

**`Permission denied (publickey)` when connecting.** The key file given to `clouduser-ssh-key` was not your public key, or you connected as the wrong user. Recreate the guest with the right key and connect as `ubuntu`.

**The guest has no internet.** Check that the default network is active (`virsh net-list`) and that your host firewall allows forwarding for `virbr0`.

## Next steps

- A web interface for many guests: [Proxmox VE on Debian 13](/guides/proxmox-ve-on-debian).
- Publish a guest's website: [Caddy reverse proxy](/guides/caddy-reverse-proxy).

## Frequently asked questions

### Do I need a graphical desktop to manage the VMs?

No. virsh and virt-install work over SSH. If you prefer a graphical tool, virt-manager on your own Linux computer can connect to the server over SSH.

### How do guests reach the internet?

Through libvirt's default network, which gives guests private addresses with DHCP and NAT behind the server's public address.

### How do I make a guest's web service public?

Run a reverse proxy such as Caddy or Nginx on the host and forward the site to the guest's private address. For other ports, forward them with firewall rules on the host.

### Can I run Windows guests?

Yes, from an installation ISO with virt-install's --cdrom option and a graphical console such as VNC over an SSH tunnel. The steps in this guide use a Linux cloud image because it needs no installer.

---

Source: <https://hyperdc.com/guides/products/kvm-virtual-machines-libvirt>\
Updated: 2026-10-15
