OpenWrt x86 Build & QEMU Run — Full Setup Documentation
This document describes the complete steps to build OpenWrt for x86_64 on Ubuntu 24.04 and run it inside QEMU with working networking, SSH, snapshots, and optional advanced features.
In this section, you are going to learn
How to prepare an Ubuntu 24.04 host system for OpenWrt x86 build ?
How to configure and build OpenWrt for x86_64 architecture ?
How to generate OpenWrt x86 disk images (ext4, squashfs, GRUB, UEFI) ?
How to run OpenWrt x86 inside QEMU with networking, SSH, and snapshots ?
How to enable KVM acceleration, UEFI boot and router-style multi-NIC simulation ?
Topics in this section,
Step 1: Host System Information
Step 2: Install Dependencies Required for OpenWrt Build
Step 3: Create Working Directory & Clone OpenWrt
Step 4: Configure OpenWrt for x86_64 Build
Step 6: Generated Build Artifacts
Step 8: Optional - Run with Two NICs (LAN + WAN)
Step 10: Optional - Create QEMU Snapshots
Step 12: Optional - Enable KVM Acceleration
Step 15: Advanced Full Router Simulation
The following host system was used for building:
Component |
Value |
|---|---|
OS |
Ubuntu 24.04.3 LTS |
Kernel |
6.8.0-86-generic |
Architecture |
amd64 |
CPUs |
8 |
RAM |
7.3 GiB |
Disk Free |
337G |
This configuration is more than sufficient for building OpenWrt.
At least 4 cores and 4 GiB of RAM are recommended for a comfortable build experience. Builds will still work on smaller machines but will be slower.
The number of CPUs directly influences how many parallel jobs (
make -j$(nproc)) you can safely run without thrashing memory or swap.Disk usage for a full OpenWrt build (including sources, toolchain, and packages) can easily exceed 30–40 GiB. Ensure free space is available on the filesystem where
~/openwrt-buildresides.If you intend to use KVM, verify that your CPU supports virtualization (Intel VT-x or AMD-V) and that it is enabled in BIOS/UEFI.
Use
lscpu,free -handdf -hto verify CPU, RAM and disk before starting long builds.
Run:
sudo apt update
sudo apt install -y build-essential libncurses-dev gawk git gettext \
libssl-dev xsltproc unzip zip python3 python3-setuptools time \
zlib1g-dev subversion file flex bison qemu-system-x86 qemu-utils \
gperf autoconf automake libtool wget ccache
(Optional) Faster rebuilds using ccache:
export CCACHE=1
build-essentialprovides the core toolchain (gcc, g++, make) required by the OpenWrt build system.libncurses-devis mandatory for themenuconfigTUI interface.gitandsubversionare used to fetch the main OpenWrt tree and various package feeds.gettext,libssl-dev,zlib1g-dev,flex,bison,gperf,autoconf,automake,libtoolare used to build the cross-toolchain and many packages that OpenWrt depends on.qemu-system-x86andqemu-utilsare only needed on the build host for running the generated images; they are not build dependencies for the firmware itself.python3andpython3-setuptoolsare used by some build scripts and helper tools.ccachecaches compilations and is very useful when you repeatedly rebuild with small configuration changes.After enabling ccache via
export CCACHE=1, you can monitor its stats withccache -s.If you are running this on a minimal/stripped Ubuntu, verify that
/usr/bin/python3exists and points to a supported Python version (3.8+ is generally fine).
mkdir -p ~/openwrt-build
cd ~/openwrt-build
git clone https://git.openwrt.org/openwrt/openwrt.git
cd openwrt
Update and install feeds:
./scripts/feeds update -a
./scripts/feeds install -a
Using a dedicated directory such as
~/openwrt-buildkeeps the build tree isolated from other projects.The default clone above pulls the current
master(development) tree.For a stable release, you can check out a specific branch or tag, e.g.:
git checkout v24.10.0
./scripts/feeds update -afetches all defined feed sources fromfeeds.conf.default(andfeeds.confif you created one)../scripts/feeds install -amakes the packages from all feeds available inmenuconfigand the build system.If you add or remove feed sources later, rerun both of the above commands.
For reproducible builds, record the exact Git commit (
git rev-parse HEAD) and keep it with your build notes.
Start menuconfig:
make menuconfig
Select the following:
Target System → x86
Subtarget → x86_64
Target Profile → Generic
Target Images:
[*] ext4
[*] squashfs
[*] Build GRUB images
[*] Build GRUB EFI images
[*] GZip images
Filesystem:
Root filesystem → ext4
Save and exit.
Target Systemcontrols the CPU architecture and ABI.x86withx86_64subtarget generates 64-bit images suitable for most modern PCs and VMs.Target Profile → Genericis a safe default for QEMU, VirtualBox, and most commodity x86 hardware. You can choose more specific profiles later if needed.Enabling both
ext4andsquashfsimages allows you to use a read-write ext4 root filesystem or a read-only squashfs with an overlay.Enabling both
Build GRUB imagesandBuild GRUB EFI imagesallows the same build tree to produce Legacy BIOS and UEFI-bootable images.The
GZip imagesoption produces compressed variants which are smaller but may add a small overhead during boot.The selected root filesystem type (ext4 here) affects how you can resize partitions and how robust the system is to unclean shutdowns.
Your configuration is stored in
.configat the root of the OpenWrt tree. Keep a backup of this file if you intend to reproduce the same image later:cp .config ~/openwrt-x86_64.config
Start the full build:
make -j$(nproc)
Typical build times:
First build: 1–2 hours
With ccache: 10–20 minutes
make -j$(nproc)automatically uses all available CPU cores. On systems with limited RAM, you may want to lower this, e.g.make -j4.The first build downloads and builds the entire cross-toolchain, toolchain libraries, and packages. This is why it takes significantly longer than subsequent builds.
If the build fails, you can often re-run
makewithout cleaning; the build system will pick up from where it stopped.To completely clean the tree, you can use:
make clean # removes built images but keeps toolchain make dirclean # removes toolchain and most downloads make distclean # almost full reset (use with care)
Log output can be saved for analysis:
make -j$(nproc) V=s | tee build.log
The
dldirectory caches downloaded sources; do not remove it unless necessary, as it speeds up future builds.
Build output directory:
~/openwrt-build/openwrt/bin/targets/x86/64/
Key files:
File |
Purpose |
|---|---|
openwrt-x86-64-generic-ext4-combined.img |
Main disk image for QEMU |
kernel.bin |
Kernel image |
rootfs.tar.gz |
Root filesystem |
*.vdi OR *.vmdk OR *.vhdx |
VM images |
Copy the main image:
cp openwrt-x86-64-generic-ext4-combined.img ~/openwrt-fresh.img
The exact filenames may include version strings or additional suffixes depending on your configuration, e.g.
*-combined-efi.imgfor UEFI.The
*-ext4-combined.imgimage usually has both kernel and rootfs in a single disk image, suitable for direct use by QEMU.kernel.binandrootfs.tar.gzare useful if you want to assemble custom disk images or use alternative boot loaders.Virtual disk formats (
*.vdi,*.vmdk,*.vhdx) are typically generated if you enable the corresponding options in menuconfig.Keeping a pristine copy such as
~/openwrt-fresh.imgis useful if you want a known-good starting point for experiments or snapshots.Always verify the image type with
file:file openwrt-x86-64-generic-ext4-combined.img
Basic run command:
qemu-system-x86_64 \
-m 1024 -smp 2 \
-drive file=openwrt-fresh.img,if=virtio,format=raw \
-nic user,model=virtio,hostfwd=tcp::2222-:22
Default OpenWrt login:
root (no password)
Default networking:
br-lan→ 192.168.1.1QEMU NAT → 10.0.2.x
-m 1024allocates 1 GiB RAM to the VM. You can reduce to 512 MiB for lighter lab setups, or increase for heavier workloads.-smp 2gives the VM 2 virtual CPUs. Many OpenWrt tasks do not require many cores, but additional vCPUs help for routing/firewalling benchmarks.if=virtioandmodel=virtiogive paravirtualized disk and NIC devices, which are faster and better supported than legacy emulated devices like e1000.-nic user,...,hostfwd=tcp::2222-:22uses QEMU’s user-mode networking and forwards host TCP port 2222 to guest port 22 (SSH).If you want a serial console instead of a graphical window, you can add:
-nographic -serial mon:stdio
When running multiple concurrent VMs, ensure each uses a unique host port (e.g. 2222, 2223, …).
Useful for router/firewall testing.
qemu-system-x86_64 \
-m 1024 -smp 2 \
-drive file=openwrt-fresh.img,if=virtio \
-device virtio-net-pci,netdev=lan \
-netdev user,id=lan,hostfwd=tcp::2222-:22 \
-device virtio-net-pci,netdev=wan \
-netdev user,id=wan
OpenWrt NIC mapping:
eth0→ LANeth1→ WAN (DHCP from QEMU)
QEMU creates two separate networks:
lan: user-mode network with port forwarding to 2222.wan: a second user-mode network that typically gets its own NATed range and DHCP server.
OpenWrt will usually bridge
eth0intobr-lan, whileeth1is treated as a WAN interface with DHCP client enabled.This setup mimics a typical home router: LAN behind the router, WAN connected to an upstream NAT.
For advanced simulations, replace
-netdev user,id=wanwith a tap device or Linux bridge to connect the WAN side to real networks.
SSH into OpenWrt from host:
ssh -p 2222 root@localhost
OpenWrt’s default SSH server is
dropbear. It listens on port 22 on the LAN side by default.On a fresh image, the
rootaccount usually has no password. The first thing you should do is set one:passwd
If you cannot connect, check from inside OpenWrt:
netstat -tlnp | grep 22 logread -e dropbear
If you remove or disable SSH access via firewall or system config, the
hostfwdoption in QEMU will still listen, but connections will fail immediately or timeout.For key-based authentication, copy your public key into
/etc/dropbear/authorized_keys.
Create snapshot:
qemu-img snapshot -c clean ~/openwrt-fresh.img
Restore:
qemu-img snapshot -a clean ~/openwrt-fresh.img
Snapshots should be created while the VM is powered off or after a clean shutdown to avoid filesystem inconsistencies.
snapshot -c NAMEcreates an internal snapshot stored in the same image file. Not all formats support snapshots equally; qcow2 is generally more snapshot-friendly than raw.To list snapshots:
qemu-img snapshot -l ~/openwrt-fresh.img
For heavy experimentation, consider converting the image to qcow2 before snapshotting:
qemu-img convert -O qcow2 openwrt-fresh.img openwrt-fresh.qcow2
Snapshots are not a backup replacement. Keep separate copies of known-good images as well.
qemu-system-x86_64 \
-m 1024 \
-smp 2 \
-drive if=pflash,format=raw,readonly=on,file=/usr/share/OVMF/OVMF_CODE.fd \
-drive if=pflash,format=raw,file=/usr/share/OVMF/OVMF_VARS.fd \
-drive file=openwrt-fresh.img,if=virtio
UEFI boot typically requires the
OVMFfirmware, provided by packages such asovmforqemu-ovmfon many distributions.OVMF_CODE.fdis the read-only code image;OVMF_VARS.fdholds modifiable NVRAM/UEFI variables.Keep a backup copy of
OVMF_VARS.fdif you want to restore a clean UEFI environment later.Ensure you built UEFI-compatible images in OpenWrt (GRUB EFI images enabled in menuconfig).
Secure Boot is generally not enabled in this simple UEFI setup; if you emulate Secure Boot, additional signing steps are required.
For significantly faster VM execution:
qemu-system-x86_64 \
-enable-kvm -cpu host \
-m 1024 -smp 2 \
-drive file=openwrt-fresh.img,if=virtio \
-nic user,model=virtio,hostfwd=tcp::2222-:22
KVM requires:
Hardware virtualization support (Intel VT-x or AMD-V).
kvmkernel modules loaded (kvm_intelorkvm_amd).
You can verify availability with:
lsmod | grep kvm [ -r /dev/kvm ] && echo "KVM available"
-cpu hostexposes the host CPU features directly to the guest, enabling better performance and instruction set usage.If you get a permission error on
/dev/kvm, add your user to thekvmgroup and re-login:sudo usermod -aG kvm "$USER"
Without
-enable-kvm, QEMU falls back to pure software emulation, which can be significantly slower.
Increase size to 2GB:
qemu-img resize openwrt-fresh.img 2G
Inside OpenWrt:
resize2fs /dev/sda2
Always shut down the VM cleanly before resizing the image file to avoid corruption.
qemu-img resizechanges the size of the virtual disk, but not the partition table or filesystem inside the guest.OpenWrt’s default partition layout usually has the root filesystem on
/dev/sda2(for combined ext4 images), but confirm with:lsblk fdisk -l /dev/sda
If there is unallocated space after the root partition, you may need to grow the partition first (using tools like
fdiskorparted) and then runresize2fs.For squashfs-based images, root is read-only and not directly resizable; ext4 images are better for experiments that need more writable space.
No Networking
Check device list:
ip a
logread -e eth
Ensure that expected interfaces (e.g.
eth0,eth1) are present and have the correct MAC addresses as per your QEMU command line.Check network configuration files:
cat /etc/config/networklogread -e ethfilters system log entries containing “eth”, which helps identify link up/down events or driver issues.From the host side, re-check QEMU options (
-nic,-netdev) for typos or conflicting settings.
OpenWrt Stuck at GRUB
Disable EFI in menuconfig:
Target Images → Uncheck EFI image
If the image was built only for EFI but QEMU is configured for legacy BIOS (or vice versa), GRUB or firmware may hang.
Double-check that your QEMU boot configuration matches what you built (BIOS vs UEFI).
For debugging, you can enable GRUB’s console and edit the boot entry at the GRUB menu to see more detailed messages.
If the disk image is corrupted, try booting with a backup image or run
fsckfrom a rescue ISO.
SSH Not Working
Validate firewall rules:
uci show firewall | grep ssh
Verify that
dropbearis enabled:/etc/init.d/dropbear statusMake sure SSH is allowed on the desired zone (typically LAN). Example rule snippet:
uci show firewall | grep 22
If you changed the SSH port, ensure your host-side port forwarding and firewall configuration match the new port.
Use
logread -e dropbearto see authentication and connection errors.From the host, test basic connectivity with:
telnet localhost 2222
qemu-system-x86_64 \
-enable-kvm -cpu host \
-m 2048 -smp 4 \
-drive file=openwrt-fresh.img,if=virtio \
-device virtio-net-pci,netdev=lan \
-netdev user,id=lan,hostfwd=tcp::2222-:22 \
-device virtio-net-pci,netdev=wan \
-netdev tap,id=wan,ifname=tap0,script=no,downscript=no
This configuration gives OpenWrt:
A LAN interface (
lan) using QEMU user-mode networking with SSH port forwarding.A WAN interface connected to a host tap interface (
tap0), which you can attach to a Linux bridge or physical NIC.
On the host, you can create and configure
tap0as follows:sudo ip tuntap add dev tap0 mode tap sudo ip link set tap0 up # Optionally bridge with another interface: # sudo ip link add name br0 type bridge # sudo ip link set tap0 master br0 # sudo ip link set eth0 master br0
This setup lets you route real traffic through the OpenWrt VM and test firewall, NAT, QoS, and VPN configurations as if it were a physical router.
Be cautious when bridging to your real network; misconfiguration can affect connectivity of your host or other machines on the LAN.
For repeatable lab scenarios, consider scripting the tap/bridge setup and teardown, and documenting IP addressing schemes used on both host and VM.