BASIC
Tested for real: I ran every command in this guide top-to-bottom on a fresh Ubuntu 24.04 virtual machine (4 CPUs, 8 GB RAM) on 13 August 2026. Versions at test time: Ubuntu 24.04.4 LTS, libvirt 10.0.0, QEMU 8.2.2, virt-manager 4.1.0, virt-install 4.1.0.
Found a problem? Tell me via the contact page.
Placeholders: this guide has nothing in CAPS-WITH-DASHES for you to replace — every command is copy-paste-safe as written. The one variable, $USER, is filled in by the shell automatically, as explained where it appears.
What you'll end up with
A small Ubuntu virtual machine running inside your Ubuntu machine. You will create it, log into it, switch it off and on, and delete it. Everything happens in the terminal, with free tools that Ubuntu itself ships.
Here is the finished state from my test run — me, logged into a machine that did not exist two minutes earlier:
first-vm login: ubuntu
Password:
Welcome to Ubuntu 24.04.4 LTS (GNU/Linux 6.8.0-136-generic x86_64)
ubuntu@first-vm:~$ hostname
first-vm
Who this is for
BASIC. One path, copy-paste-safe, nothing to edit mid-line. For a desktop or laptop Ubuntu user who has never made a virtual machine before.
Time and cost
The commands took me about 4 minutes of actual run time, measured. The big install is 143 MB and took 85 seconds. The Ubuntu image is 596 MB and took 9 seconds on my line — your download times depend on your connection. Budget an hour to read and understand as you go.
Cost: nothing. Every tool here is free and comes from Ubuntu's own archive.
One honesty note: my test machine is itself a virtual machine, which is the slowest way to do this. On real hardware this may be faster, but disk, network, and CPU vary.
Words you'll meet
- Virtual machine (VM) — a complete computer, made of software, running inside your real one. Also called a guest. Your real machine is the host. (basics)
- Hypervisor — the software that creates and runs virtual machines. On Linux it is called KVM, and it is built into the kernel you are already running. (basics)
- Disk image — one big file on your host that the guest sees as its hard drive. Ours ends in
.qcow2. (basics) - Cloud image — a ready-installed Ubuntu disk image, published by Ubuntu. Using one skips the whole installer. (basics)
- cloud-init — the first-boot setup tool inside cloud images. We hand it a tiny settings file and it sets our password. (basics)
- Console — the guest's own screen and keyboard, reached from your terminal. Like plugging a monitor into the virtual machine. (basics)
- Snapshot — a saved moment of a VM you can return to. Not in this guide — the next one covers it properly. (basics)
- sudo — "do this as the administrator". Ubuntu asks for your password when you use it. (basics)
Before you start
- An Ubuntu machine (I tested 24.04) with a user that can use
sudo. - About 1.5 GB of free disk space (625 MB of tools, 596 MB of image, plus the VM's disk).
- 8 GB of RAM or more is comfortable. The guest takes 2 GB while it runs.
The steps
Step 1 — check your machine can do this
Your CPU needs hardware virtualisation support. Most modern CPUs have it. Ubuntu has a tiny tool that gives a straight yes or no.
Install it:
sudo apt-get install -y cpu-checker
The important line is:
Setting up cpu-checker (0.7-1.3build2) ...
Check it worked. Ask the question:
kvm-ok
You should see something like:
INFO: /dev/kvm exists
KVM acceleration can be used
KVM acceleration can be used is the green light. If it says KVM can NOT be used, virtualisation is probably switched off in your machine's firmware settings (often called VT-x or AMD-V) — turn it on there and re-run kvm-ok before going further.
Step 2 — install the virtualisation tools
Four packages give us everything: the emulator (qemu-kvm), the management service (libvirt-daemon-system), the graphical manager (virt-manager) and the terminal creation tool (virtinst). All from Ubuntu's own archive — no third-party repositories.
sudo apt-get update
sudo apt-get install -y qemu-kvm libvirt-daemon-system virt-manager virtinst
Your output will vary, but look for:
11 upgraded, 239 newly installed, 0 to remove and 15 not upgraded.
Need to get 143 MB of archives.
After this operation, 625 MB of additional disk space will be used.
[...snipped...]
This was the longest wait of my whole test: 85 seconds.
Check it worked. Ask the management tool for its version:
virsh --version
You should see something like:
10.0.0
Your version may be newer — that's fine.
Step 3 — join the libvirt group, then log out and back in
This step has a trap that confused me too, so let me spell it out. There are actually two hypervisors after install: a system-wide one where real work happens, and a private per-user one. Until your user joins the libvirt group, the tools quietly talk to the private one. Commands "work", but your machines end up in the wrong place, or seem to vanish.
Add yourself to the group. $USER fills in your username automatically — nothing to edit:
sudo adduser $USER libvirt
The important line is:
info: Adding user `tester' to group `libvirt' ...
Now the part everyone skips: log out and log back in. Group changes only apply to new logins. (Same dance as the docker group, if you have met that.)
After logging back in, point the tools at the system hypervisor by default, permanently:
echo 'export LIBVIRT_DEFAULT_URI=qemu:///system' >> ~/.bashrc
Load it into your current terminal:
source ~/.bashrc
Check it worked. Two quick questions:
groups
Your output will vary, but look for:
tester libvirt
libvirt must be in the list. Then:
virsh uri
The important line is:
qemu:///system
qemu:///system — the system hypervisor. If it says qemu:///session, the .bashrc line did not load; log out and in once more.
Step 4 — check the VM network is on
Guests reach the internet through a small private network that libvirt manages. On Ubuntu 24.04 it came switched on and set to start at boot — this step only confirms it.
virsh net-list --all
You should see something like:
Name State Autostart Persistent
--------------------------------------------
default active yes yes
Check it worked. The default row says active and Autostart yes. If State says inactive, run virsh net-start default and virsh net-autostart default, then look again.
Step 5 — download Ubuntu, pre-installed
Here is the trick that makes this guide drama-free. Ubuntu publishes cloud images: disk images with Ubuntu already installed. We download one instead of clicking through an installer for half an hour.
wget https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img
Your output will vary, but look for:
[...snipped: progress bar...]
589824K ........ ........ ... 100% 72.9M=8.2s
It is 596 MB — mine took 9 seconds on a fast line, yours may take a few minutes.
Check it worked. Inspect the file:
qemu-img info noble-server-cloudimg-amd64.img
A passing run includes:
image: noble-server-cloudimg-amd64.img
file format: qcow2
virtual size: 3.5 GiB (3758096384 bytes)
disk size: 595 MiB
file format: qcow2 — a real disk image, ready to boot.
Step 6 — put the disk in place and give it room
Copy the image into libvirt's image folder, under the name our VM will use:
sudo cp noble-server-cloudimg-amd64.img /var/lib/libvirt/images/first-vm.qcow2
The image ships with a tiny 3.5 GB virtual disk. Grow it to 10 GB — the guest expands into it on first boot:
sudo qemu-img resize /var/lib/libvirt/images/first-vm.qcow2 10G
The important line is:
Image resized.
Check it worked. List the folder — note the sudo, the folder is private to root:
sudo ls -lh /var/lib/libvirt/images/
You should see something like:
-rw-r--r-- 1 root root 596M Aug 13 16:50 first-vm.qcow2
Step 7 — write the first-boot settings
Cloud images have no password set — on purpose. We pass one in with a small cloud-init settings file. Make a folder and the file:
mkdir -p ~/first-vm
Create the file with one copy-paste block:
printf '#cloud-config\nhostname: first-vm\npassword: MyFirstVM2026\nchpasswd:\n expire: false\n' > ~/first-vm/user-data
That sets the machine's name to first-vm and the password for the built-in ubuntu user to MyFirstVM2026. It is a practice password for a practice machine. Do not reuse this password. This VM is reachable from your libvirt private network. Change it after your first login if you keep the VM around.
Now pack the file into a tiny seed image the VM can read at boot. Ubuntu ships a purpose-built tool for exactly this:
sudo apt-get install -y cloud-image-utils
cloud-localds ~/first-vm/seed.img ~/first-vm/user-data
Put it next to the disk:
sudo cp ~/first-vm/seed.img /var/lib/libvirt/images/first-vm-seed.img
Check it worked. Ask what the seed file is:
file ~/first-vm/seed.img
A passing run includes:
/home/tester/first-vm/seed.img: ISO 9660 CD-ROM filesystem data 'cidata'
'cidata' is the label cloud-init looks for.
Step 8 — create the virtual machine
One command creates and starts the VM: 1 CPU, 2 GB of RAM, our disk, and the seed attached like a CD. The \ at each line end lets one command span several lines — copy the whole block as one:
virt-install \
--name first-vm \
--memory 2048 \
--vcpus 1 \
--disk path=/var/lib/libvirt/images/first-vm.qcow2 \
--disk path=/var/lib/libvirt/images/first-vm-seed.img,device=cdrom \
--osinfo ubuntu24.04 \
--import \
--network network=default \
--graphics none \
--noautoconsole
You should see something like:
WARNING Requested memory 2048 MiB is less than the recommended 3072 MiB for OS ubuntu24.04
Starting install...
Creating domain... | 0 B 00:00
Domain creation completed.
That WARNING is real and safe to ignore here — 2 GB boots and runs fine for a learning VM. In my test the whole creation took 6 seconds, and the guest was on the network 35 seconds later.
Check it worked. List your machines:
virsh list --all
A passing run includes:
Id Name State
--------------------------
1 first-vm running
running — you own a virtual machine.
Step 9 — log in on the console
Give it about a minute after creation — the guest needs to boot and cloud-init needs to set your password. Then connect to its console:
virsh console first-vm
You should see something like:
Connected to domain 'first-vm'
Escape character is ^] (Ctrl + ])
If the screen then sits empty, press Enter once — that wakes the login prompt. This caught me too. Log in with user ubuntu, password MyFirstVM2026:
first-vm login: ubuntu
Password:
Welcome to Ubuntu 24.04.4 LTS (GNU/Linux 6.8.0-136-generic x86_64)
Have a look around — it is a complete Ubuntu machine:
hostname
The important line is:
first-vm
To leave: type exit to log out, then hold Ctrl and press ] to drop back to your own terminal.
Check it worked. You saw the first-vm hostname from inside the guest, and you are back at your own prompt.
Step 10 — switch it off and on
A polite shutdown, like pressing the power button:
virsh shutdown first-vm
You should see something like:
Domain 'first-vm' is being shutdown
Mine took 4 seconds to stop. Confirm with virsh list --all — the State shows shut off. Start it again any time:
virsh start first-vm
The important line is:
Domain 'first-vm' started
Check it worked. virsh list --all shows running again. Full boot back to a login prompt takes about half a minute.
That completes part one: you built, entered, and power-cycled a virtual machine, all in the terminal. Part two below covers the optional graphical view, the problems I hit, and the full undo.
Part two — the graphical view, and cleaning up
Everything above was the tested terminal path. This part is shorter: what virt-manager gives you, the failure modes I actually hit, and how to remove it all. Come back to it whenever suits — the VM from part one keeps running either way.
What about the graphical way?
virt-manager, which we installed in Step 2, is the point-and-click version of everything above. Honesty first: my test machine has no desktop. I verified the program is installed (virt-manager --version answered 4.1.0). I could not test the GUI itself, and I will not fake screenshots.
Documented behaviour, not tested in my run: virt-manager normally shows a window listing your machines — first-vm among them — with usage graphs. Double-clicking a VM should open its screen, and the toolbar carries the usual start, pause and stop controls; the hardware details live in the VM's own window. The exact layout can differ between versions, so treat this as orientation, not a map. Everything this guide did in the terminal, virt-manager can do with clicks — and the VM you built here will already be in its list.
Something went wrong?
Everything below is something I actually hit or provoked during testing.
- You see an empty list from
virsh list --allbut you created a VM → it means your tools are talking to the per-user hypervisor, not the system one → runvirsh uri; if it saysqemu:///session, redo Step 3, including the log-out. - You see
Failed to connect socket ... libvirt-sock: Permission denied→ it means you are not in thelibvirtgroup yet in this session → redo Step 3 and genuinely log out and back in. - You see
WARNING Requested memory 2048 MiB is less than the recommended 3072 MiB→ it means virt-install is being cautious → nothing to do; the VM runs fine for learning. - You see
ls: cannot open directory '/var/lib/libvirt/images/': Permission denied→ it means that folder is root-only, which is normal → putsudoin front of thels. - You see a blank screen after
virsh console→ it means the login prompt already scrolled past or has not printed → press Enter once; if the guest is still booting, wait 30 seconds and press Enter again.
Undo all of this
Destructive if this machine already had VMs: the package purge and the final folder deletion remove libvirt's data for every VM on the machine, not just first-vm. Follow the whole section only on a machine where libvirt exists just for this guide.
Tested for real, in this order. It removes the VM, its disks, the tools, and the group change.
Force the VM off (fine for a throwaway):
virsh destroy first-vm
Delete it, including both disk files:
virsh undefine first-vm --remove-all-storage
A passing run includes:
Domain 'first-vm' has been undefined
Volume 'vda'(/var/lib/libvirt/images/first-vm.qcow2) removed.
Volume 'sda'(/var/lib/libvirt/images/first-vm-seed.img) removed.
Remove the downloaded image and the settings folder:
rm -r ~/first-vm ~/noble-server-cloudimg-amd64.img
Remove the .bashrc line and leave the group:
sed -i '/LIBVIRT_DEFAULT_URI/d' ~/.bashrc
sudo gpasswd -d $USER libvirt
Remove the packages:
sudo apt-get remove --purge -y qemu-kvm libvirt-daemon-system virt-manager virtinst cloud-image-utils cpu-checker
sudo apt-get autoremove --purge -y
Now an honest wrinkle I hit: the qemu engine packages survive that sweep — apt does not count them as removable leftovers. Purge them explicitly:
sudo apt-get remove --purge -y qemu-system-x86 qemu-system-gui qemu-system-common qemu-utils qemu-block-extra ipxe-qemu
sudo apt-get autoremove --purge -y
Remove the leftover network bridge and data folders. Only do this if libvirt was installed just for this guide — these folders hold the disks and configs of every VM on the machine:
sudo ip link delete virbr0
sudo rm -r /var/lib/libvirt /etc/libvirt
Check it worked. Ask for the tools one last time:
virsh --version
The important line is:
bash: virsh: command not found
That error is the pass mark. My whole undo took about 75 seconds and freed roughly 620 MB.
Where to go next
- Headless VMs: virt-install, cloud-init and snapshots without a desktop (the DEEP END follow-on — SSH keys instead of passwords, snapshots you can rewind to, and cloning).
- Official docs: Ubuntu Server virtualisation documentation — the reference this guide's install follows.
Last tested: 13 August 2026 on Ubuntu 24.04.4 LTS. Versions: libvirt 10.0.0, QEMU 8.2.2, virt-manager 4.1.0, virt-install 4.1.0, cloud-image-utils 0.33.
Tried it? Improved it?
Tell the forum what worked and what didn’t: real experience beats recommendations, and the best answers get folded back into this guide with credit.
Related guides
SSH keys, properly: never type a server password again
Your laptop will hold a small pair of files called an SSH key. Your server will trust it, so logging in never asks for a password — then we turn password logins off completely.
12 min read
First hour with a new Ubuntu server: locking the doors
A fully updated server, a personal account with admin rights, a firewall that blocks everything except SSH, and security updates that install themselves.
10 min read