Ubuntu-24.04 Minimal Server on KVM/Libvirt Default NAT: Network Troubleshooting and Docker Deployment Full Record
This post is the complete record of placing Ubuntu 24.04 Minimal Server into a KVM/libvirt default NAT network — going from “apt update completely fails” to “Docker Engine running normally” — a deployment walkthrough you can follow step by step.
The focus is not just providing a set of commands, but documenting every checkpoint clearly, to avoid getting stuck at the same place when rebuilding the environment later.
Setting Up the Virtualization Server (Arch-Derived)
First, install virtualization and networking packages on the Host.
Host:
|
|
Purpose:
virt-manager: Graphical VM management.qemu-desktop: KVM/QEMU hypervisor.libvirt: VM and virtual network management core.edk2-ovmf: UEFI firmware support.dnsmasq: Used bydefault NATfor DHCP/DNS.
Start the service:
Host:
|
|
Add the current user to the libvirt group:
Host:
|
|
Takes effect after re-login.
Installer Options That Matter Later
During the installation process, at minimum confirm:
- IPv4 is enabled.
- IPv6 is enabled.
- Select OpenSSH server installation.
- Network mode is
libvirt default NAT.
If OpenSSH server isn’t installed initially, you’ll only be able to type commands in the VM console, which is very inefficient.
Missing Tools Are Normal After a Lean Setup
After installation, seeing the following status is no cause for alarm — this is by design:
- No
ping. - No
vim. - No
nano. - No
net-tools. - Usually only
ipis available.
So the initial troubleshooting core will be ip addr, ip route, /proc/net/route, /etc/resolv.conf.
Symptom: apt update Fails
VM:
|
|
Typical error:
|
|
This error looks like DNS on the surface, but in practice it’s often “routing + DNS both broken simultaneously.”
Diagnostic Checklist Before Making Changes
Identify the Real Interface Name
VM:
|
|
Need to capture:
- The real interface name (e.g.,
enp0s1,ens1,enp1s0). - Don’t assume it’s always
eth0.
Ubuntu 24.04 naming may differ across virtualization platforms. Writing the wrong interface name in commands will invalidate all subsequent configuration.
Scope-Local v6 Addresses: Seeing inet6 Does Not Imply WAN Reachability
A common situation in ip addr is:
- Interface has
inet6. - But no usable
inet(IPv4).
This usually means IPv4 DHCP didn’t get an address, leaving only IPv6 link-local. It’s a warning sign of “network not fully connected,” not a normal internet-accessible state.
Confirm the Fallback Route Exists
VM:
|
|
If /proc/net/route has almost nothing but headers, or ip route has no default via ..., it means the kernel doesn’t know where to send packets.
Inspect the Resolver Configuration
VM:
|
|
If nameserver is empty, invalid, or points to an unreachable target, apt will get stuck at domain resolution.
Find the Hypervisor Bridge Address
Host:
|
|
Find virbr* interfaces, usually virbr0, but names aren’t guaranteed to be fixed.
Then check its IPv4, for example:
|
|
This address is the VM’s gateway candidate.
Check Packet Relaying and Masquerade Rules
Check bridge status:
Host:
|
|
Check IP forwarding:
Host:
|
|
If it shows:
|
|
The VM almost certainly cannot reach the internet through the Host.
Check FORWARD chain:
Host:
|
|
Check if policy is DROP and whether virbr packet forwarding is allowed.
Check NAT POSTROUTING:
Host:
|
|
Need to see something like:
|
|
Without MASQUERADE, even with a gateway, the VM often can’t reach the external network.
Virtualization-Server Fixes
Enable IP forwarding:
Host:
|
|
Set FORWARD policy to ACCEPT:
Host:
|
|
Add NAT rule:
Host:
|
|
Re-verify:
Host:
|
|
Guest-OS Manual Connectivity Procedure
First confirm two things:
- VM NIC name, e.g.,
enp1s0. - Host
virbrIP, e.g.,192.168.122.1.
Manually assign IP:
VM:
|
|
Create same-subnet route:
VM:
|
|
Add default gateway:
VM:
|
|
Check routing:
VM:
|
|
Need to see default via 192.168.122.1 dev enp1s0.
Force-inject DNS:
VM:
|
|
Connectivity test:
VM:
|
|
Once this step works, subsequent tool and service installation can proceed.
Remote Shell for Better Ergonomics Than the Viewer
A common pain point is that the VM console can’t smoothly copy-paste, making long commands error-prone.
First install core tools:
VM:
|
|
Start SSH:
VM:
|
|
Connect from Host:
Host:
|
|
After this, use your local terminal for VM operations — much more efficient.
Container Engine Setup: Official Source Instead of docker.io
Ubuntu’s built-in docker.io package works, but this time I used the Docker official source instead. Reasons:
- Version updates are usually faster.
buildxandcompose v2plugin integration is more complete.- Security update cadence stays in sync with official.
Incremental Container Runtime Installation
Create keyring directory:
VM:
|
|
Import Docker GPG:
VM:
|
|
Add Docker repository:
VM:
|
|
Install Docker Engine and plugins:
VM:
|
|
Set up sudo-less Docker (current user):
VM:
|
|
Verify:
VM:
|
|
Validation Checklist at the End
Host-side:
libvirtdis running normally.virbr*bridge exists with IPv4.net.ipv4.ip_forward = 1.- FORWARD chain allows VM packet forwarding.
- NAT has MASQUERADE for the VM subnet.
VM-side:
- Interface has valid IPv4.
- Routing table has
default via <virbr IP>. /etc/resolv.confhas a usable nameserver.apt updatesucceeds.- SSH login works.
- Docker
hello-worldruns successfully.
This completes the entire path from “Minimal initially unusable” to “operationally manageable, container-deployable.”