Introduction
I wanted to learn how OpenWrt works as a router without buying additional hardware or disturbing my existing homelab network.
My approach was to run OpenWrt as an Incus container and connect a separate Debian container as its LAN client.
The exercise covered several practical networking concepts:
- Creating an OpenWrt container.
- Connecting WAN and LAN interfaces.
- Configuring a static LAN address and DHCP.
- Understanding firewall zones, forwarding, and NAT.
- Troubleshooting Internet access in a network that requires an HTTP proxy.
- Making the LuCI web interface accessible from the host using an Incus proxy device.
This is a learning lab, not a production router configuration.
1. Lab architecture
The topology consists of three main components:
- The Debian host running Incus.
- An OpenWrt container acting as the router.
- A Debian container acting as a LAN client.
The OpenWrt WAN interface connects to an existing Incus bridge. Its LAN interface connects to a newly created, isolated Incus network.
Existing upstream network
|
Incus host bridge
|
OpenWrt WAN
|
OpenWrt container
WAN | LAN
|
Isolated LAN bridge
|
Debian client
Browser on host
|
127.0.0.1:8080
|
Incus proxy device
|
OpenWrt:80 (LuCI)
Example IP addressing
The following table uses example addresses instead of the original lab addresses.
| Component | Example address | Purpose |
|---|---|---|
| Incus upstream gateway | 192.168.50.1 |
Existing upstream network |
| OpenWrt WAN | 192.168.50.10 |
Router's upstream interface |
| OpenWrt LAN | 192.168.60.1 |
Default gateway for LAN clients |
| Debian client | 192.168.60.100 |
DHCP-assigned address |
| LuCI host endpoint | 127.0.0.1:8080 |
Browser access from the host |
These are illustrative private addresses. Your WAN address will normally be assigned by DHCP, so do not configure the example WAN address blindly.
The 192.0.2.0/24, 198.51.100.0/24, and 203.0.113.0/24 ranges are reserved for documentation. They are not substitutes for real, routable public IP addresses.
Security note: Do not publish your actual internal addressing, proxy address, credentials, or other sensitive network details unless you have a reason to disclose them.
2. Create the OpenWrt container
Start by launching an OpenWrt image:
incus launch images:openwrt/25.12 openwrt
Check the container status:
incus list
incus info openwrt
Inspect the network interfaces:
incus exec openwrt -- ip link
incus exec openwrt -- ip addr
incus exec openwrt -- ip route
The OpenWrt image used in this lab was from the OpenWrt 25.12 series.
Image availability and package names can change between releases. Check the image and release documentation before reproducing this on another system.
3. Connect the WAN interface
The OpenWrt container initially connects to the existing Incus bridge.
In this lab, the WAN interface is named eth0 and uses DHCP.
Inspect the OpenWrt network configuration:
incus exec openwrt -- cat /etc/config/network
The relevant configuration is:
config interface 'wan'
option device 'eth0'
option proto 'dhcp'
Check the resulting address and route:
incus exec openwrt -- ip addr
incus exec openwrt -- ip route
A working WAN configuration should have an address and a default route.
For example:
default via 192.168.50.1 dev eth0
The gateway above is illustrative; use the route actually assigned in your environment.
Test connectivity to the upstream gateway:
incus exec openwrt -- ping -c 3 192.168.50.1
Replace the example gateway with the actual one shown by ip route.
A successful gateway ping confirms local reachability to that gateway. It does not prove that unrestricted Internet access is available.
4. Create an isolated LAN network
Rather than modifying an existing Incus bridge, create a dedicated network for the OpenWrt LAN.
incus network create owrt-lan \
ipv4.address=none \
ipv6.address=none
This creates a bridge without an Incus-managed IPv4 or IPv6 gateway address.
Attach a second NIC to OpenWrt:
incus config device add openwrt lan0 nic \
network=owrt-lan \
name=eth1
Verify that the new interface exists:
incus exec openwrt -- ip link
The intended interface layout is:
-
eth0: WAN -
eth1: LAN
Interface names can vary by image and configuration. Confirm them before applying network settings.
5. Configure the OpenWrt LAN address
Configure the LAN interface with a static address.
For this example, the LAN subnet is 192.168.60.0/24, and the router address is 192.168.60.1.
incus exec openwrt -- ash -c '
uci set network.lan="interface"
uci set network.lan.device="eth1"
uci set network.lan.proto="static"
uci set network.lan.ipaddr="192.168.60.1"
uci set network.lan.netmask="255.255.255.0"
uci commit network
'
Reload the network configuration:
incus exec openwrt -- /etc/init.d/network reload
Verify the LAN address:
incus exec openwrt -- ip addr
incus exec openwrt -- ip route
Expected route:
192.168.60.0/24 dev eth1 scope link
This subnet is separate from the upstream network. Keeping the WAN and LAN subnets distinct is essential for a conventional routed configuration.
6. Enable DHCP for LAN clients
Configure OpenWrt to assign addresses to clients on the LAN.
incus exec openwrt -- ash -c '
uci set dhcp.lan="dhcp"
uci set dhcp.lan.interface="lan"
uci set dhcp.lan.start="100"
uci set dhcp.lan.limit="100"
uci set dhcp.lan.leasetime="12h"
uci set dhcp.lan.ignore="0"
uci commit dhcp
'
The DHCP pool starts at the router's LAN address plus 100 addresses in the configured subnet. For this example, clients can receive addresses starting at 192.168.60.100.
Reload the relevant service:
incus exec openwrt -- /etc/init.d/dnsmasq restart
Check that the service is running:
incus exec openwrt -- /etc/init.d/dnsmasq status
Start a Debian client
incus launch images:debian/13 owrt-client \
--network owrt-lan
Inspect the client address and routing table:
incus exec owrt-client -- ip addr
incus exec owrt-client -- ip route
The client should receive a DHCP address in the LAN subnet and a default route through 192.168.60.1.
Test the connection to OpenWrt:
incus exec owrt-client -- ping -c 3 192.168.60.1
In this lab, DHCP worked and the client could ping the OpenWrt LAN address.
7. Understand the firewall and NAT configuration
OpenWrt uses firewall zones to control traffic between interfaces.
The relevant default design is:
- LAN input: ACCEPT
- LAN output: ACCEPT
- LAN forwarding: ACCEPT
- WAN input: REJECT
- WAN output: ACCEPT
- WAN forwarding: DROP
- LAN-to-WAN forwarding: enabled
- WAN masquerading: enabled for IPv4
Inspect the configuration:
incus exec openwrt -- uci show firewall
Check the generated firewall rules:
incus exec openwrt -- fw4 print
Look for a LAN-to-WAN forwarding rule and an IPv4 masquerading rule.
The conceptual flow is:
LAN client
|
| Source: 192.168.60.100
v
OpenWrt LAN
|
| Forwarding: LAN -> WAN
| IPv4 source NAT (masquerading)
v
OpenWrt WAN
|
v
Upstream network
The addresses are examples.
NAT allows multiple private LAN addresses to share an upstream IPv4 address. However, NAT is not an Internet access policy bypass. It cannot override an upstream firewall, an egress restriction, or a requirement to use an HTTP proxy.
8. Troubleshoot Internet access
This was the most instructive part of the exercise.
The Debian client could reach the OpenWrt router, and DNS resolution returned a result. However, direct HTTPS requests timed out.
Examples of the direct tests were:
incus exec owrt-client -- \
curl -4 -I --connect-timeout 5 --max-time 8 \
https://downloads.openwrt.org/
The request timed out.
A direct IPv6 attempt also failed. That result alone does not establish why IPv6 was unavailable; the lab did not have a demonstrated global IPv6 route.
Ping tests to a public IPv4 address also received no replies:
incus exec owrt-client -- ping -c 3 1.1.1.1
incus exec openwrt -- ping -c 3 1.1.1.1
Neither test received replies.
Ping failure is not conclusive proof of broken routing because ICMP may be filtered. Direct HTTPS timeouts are another useful signal, but they do not identify the precise cause by themselves.
Discover the upstream HTTP proxy
The host's environment used an HTTP proxy. For this article, the proxy address is intentionally represented as a placeholder:
<PROXY_HOST>:<PROXY_PORT>
Do not copy the placeholder literally. Substitute a proxy that your network administrator has authorised you to use.
Test from the host:
curl -x http://<PROXY_HOST>:<PROXY_PORT> \
-I --connect-timeout 5 --max-time 10 \
https://downloads.openwrt.org/
Then test from the LAN client:
incus exec owrt-client -- \
curl -x http://<PROXY_HOST>:<PROXY_PORT> \
-I --connect-timeout 5 --max-time 10 \
https://downloads.openwrt.org/
In the original lab, both requests returned HTTP/2 200.
The client could also download the page through the proxy:
incus exec owrt-client -- \
curl -x http://<PROXY_HOST>:<PROXY_PORT> \
-fL --connect-timeout 5 --max-time 20 \
https://downloads.openwrt.org/ \
-o /tmp/openwrt-index.html
This downloaded the page successfully.
What did this prove?
It established that:
- The client could reach the proxy.
- The proxy could retrieve the requested HTTPS content.
- The client could use the proxy even though direct HTTPS access timed out.
It did not prove that every firewall or NAT path was correct, nor did it establish the exact reason direct traffic failed.
The important distinction is that an HTTP proxy is an application-layer service. Ordinary routing and NAT do not automatically redirect every HTTP or HTTPS connection through it.
For an HTTP CONNECT proxy, clients normally need explicit proxy configuration unless a separately designed and compatible interception mechanism is deployed.
Avoid placing proxy credentials in shell history, public repositories, screenshots, or published logs.
9. Install and start LuCI
LuCI is OpenWrt's web administration interface. The web server commonly used by OpenWrt is uhttpd.
Check whether the relevant components are installed:
incus exec openwrt -- sh -c '
opkg list-installed | grep -E "^(luci|uhttpd)"
'
Check whether uhttpd is available and running:
incus exec openwrt -- sh -c '
command -v uhttpd
/etc/init.d/uhttpd status
'
If LuCI is missing, package installation may be required.
incus exec openwrt -- opkg update
incus exec openwrt -- opkg install luci
These commands require working package-repository access. In a proxy-restricted environment, opkg may need additional configuration or an alternative authorised package-delivery method. A successful curl through a proxy does not automatically configure opkg to use that proxy.
Once installed, enable and restart uhttpd:
incus exec openwrt -- /etc/init.d/uhttpd enable
incus exec openwrt -- /etc/init.d/uhttpd restart
Check listening sockets:
incus exec openwrt -- netstat -lntp
If netstat is unavailable, use whatever socket-inspection utility is included in the image.
Confirm the actual listening port and address before configuring the proxy device. This example assumes LuCI is available over HTTP on port 80.
10. Expose LuCI through an Incus proxy device
The goal is to open LuCI from the host without exposing the management interface to the entire LAN or Internet.
Use an Incus proxy device bound to the host's loopback interface.
On the Incus host:
incus config device add openwrt luci-proxy proxy \
listen=tcp:127.0.0.1:8080 \
connect=tcp:192.168.50.10:80
Replace 192.168.50.10 with the actual OpenWrt WAN address assigned in your lab.
This command establishes the following connection:
Host browser
|
| http://127.0.0.1:8080
v
Incus proxy device
|
| TCP forwarding
v
OpenWrt WAN address:80
|
v
uhttpd / LuCI
Because the listener is bound to 127.0.0.1, it is intended for access from the host itself.
Check the device configuration:
incus config device show openwrt
Test it from the host:
curl -I --connect-timeout 5 http://127.0.0.1:8080/
Then open the following address in a browser on the Incus host:
http://127.0.0.1:8080/
The browser should reach LuCI if the interface is installed, uhttpd is listening on the configured destination, and the connection is permitted.
If the test fails, check the listener, destination address, uhttpd status, and firewall policy. OpenWrt normally restricts incoming connections from the WAN zone, so do not disable the firewall globally just to make LuCI work.
Important security considerations
- Keep the proxy listener on
127.0.0.1when only host access is required. - Do not bind LuCI to a public address.
- Do not expose the OpenWrt administration interface directly to the Internet.
- Use HTTPS and appropriate access controls when designing remote administration.
- If other LAN computers need access, prefer a deliberate management network or a controlled tunnel rather than exposing the listener indiscriminately.
A loopback-only listener is not reachable directly from other computers on the network.
11. Troubleshooting checklist
When something fails, isolate the layer instead of changing several settings at once.
Container and interfaces
incus list
incus exec openwrt -- ip addr
incus exec openwrt -- ip route
DHCP and client routing
incus exec owrt-client -- ip addr
incus exec owrt-client -- ip route
Firewall
incus exec openwrt -- uci show firewall
incus exec openwrt -- fw4 print
Proxy access
incus exec owrt-client -- \
curl -x http://<PROXY_HOST>:<PROXY_PORT> \
-I --connect-timeout 5 --max-time 10 \
https://downloads.openwrt.org/
LuCI
incus exec openwrt -- /etc/init.d/uhttpd status
incus config device show openwrt
curl -I --connect-timeout 5 http://127.0.0.1:8080/
Replace the proxy placeholder and destination IP with values appropriate to your environment.
12. Lessons learned
This exercise reinforced several practical networking concepts.
A working LAN does not mean the Internet is reachable. DHCP, local routing, DNS, upstream routing, NAT, and proxy access are separate concerns.
NAT is not a proxy. Masquerading rewrites source addresses; it does not automatically route web requests through an HTTP proxy.
A failed ping does not always mean routing is broken. ICMP may be blocked, so test the actual service you need as well.
Use isolated networks for experiments. Creating a dedicated Incus bridge avoids changing unrelated homelab networks.
Management interfaces deserve extra protection. Binding a local port forward to 127.0.0.1 is a useful way to access LuCI from the host without publishing it to other machines.
Most importantly, troubleshoot with evidence. Inspect the routes, check the firewall configuration, test direct connectivity, test proxy connectivity, and verify the web server before changing the system.
Conclusion
Running OpenWrt inside Incus provided a practical way to explore router behaviour using existing hardware.
The lab progressed from a container with a WAN interface to an isolated LAN with DHCP, a Debian client, firewall forwarding, and a local LuCI access path.
The network's proxy requirement was a useful reminder that the real world does not always behave like a simple home-router diagram. Understanding the difference between routing, NAT, and application-layer proxies is just as important as getting the configuration to work.
For a learning lab, small experiments and careful verification are more valuable than making broad changes until something happens to work.
Top comments (0)