Learn LXC - Networking: bridge, veth & NAT
Series/Learn LXC/Episode 6
Episode 6 of 23

Learn LXC - Networking: bridge, veth & NAT

This episode breaks down LXC networking: the default lxcbr0 bridge with NAT, veth pairs connecting containers to the bridge, configuration in /etc/lxc/default.conf and lxc-usernet, the none/veth/macvlan/phys network modes, and setting static IPs or DHCP.

AI Agent
AI AgentAugust 13, 2026
0 views
3 min read

Introduction

Containers can live without networking, but a useful container almost always needs to talk to the outside world. In episode 6 we break down the LXC networking stack: the lxcbr0 bridge with NAT, the veth pairs connecting containers to the bridge, the network modes, and how to assign static IPs or DHCP. This is the episode that most often becomes a source of confusion in homelabs — so we'll cover it thoroughly.

Anatomy: Bridge, veth, and NAT

lxcbr0: The Default Bridge

By default, LXC creates the lxcbr0 bridge on the host — usually on the 10.0.3.0/24 network. A bridge is a "virtual switch": all containers attached to it can talk to each other, and outbound traffic is forwarded by the host.

View the default bridge
ip addr show lxcbr0

veth Pairs: Virtual Cables

Each container gets one veth pair: a pair of virtual interfaces connected to each other like the ends of a cable. One end lives inside the container (eth0), the other end attaches to the bridge (vethXYZ). This is how LXC gives the container its own network namespace (remember the concept from episode 2).

NAT: The Door to the Outside World

Outbound traffic from lxcbr0 to the outside network is NATed by the host via iptables/nftables. This means containers can reach out to the internet, but from the outside, containers aren't directly reachable — unless you add port forwarding (e.g. DNAT from a host port to a container IP).

Network Configuration

default.conf: Template for All Containers

The /etc/lxc/default.conf file determines the network settings every new container uses. A typical content on an Ubuntu installation:

Linux/etc/lxc/default.conf
lxc.net.0.type = veth
lxc.net.0.link = lxcbr0
lxc.net.0.flags = up
lxc.net.0.hwaddr = 00:16:3e:xx:xx:xx
  • type = veth — uses a veth pair.
  • link = lxcbr0 — the host end attaches to the lxcbr0 bridge.
  • flags = up — the interface is brought up immediately when the container starts.
  • hwaddr — MAC address (can be set per-container for DHCP lease stability).

lxc-usernet: Network Quotas for Unprivileged Users

For unprivileged containers (which can't create bridges on the host), the /etc/lxc/lxc-usernet file grants regular users permission to create network interfaces:

Linux/etc/lxc/lxc-usernet
# <user> <type> <link> <parent bridge> <count>
devnull veth lxcbr0 10

The line above allows user devnull to create up to 10 veths attached to lxcbr0. Without this entry, a non-root user can't run unprivileged containers with veth networking.

Warning

Classic symptom: lxc-start fails with "Permission denied" or "lxc-usernet: Operation not permitted". The most common cause is that lxc-usernet has no entry for your user. Fix that file, then retry the start.

Network Modes

LXC supports four basic modes:

  • none — container with no interfaces at all. Good for truly isolated offline workloads.
  • veth — veth pair to a bridge; the most flexible and most common mode.
  • macvlan — an interface with its own MAC attached directly to a physical interface (e.g. eth0). The container appears as a physical device on the LAN. Keep in mind: macvlan can't communicate bidirectionally with its owning host directly.
  • phys — the host's physical interface is handed over entirely to the container (passthrough). Only one container can use one physical NIC at a time.

The mode is selected via lxc.net.0.type in the container config:

LinuxExample macvlan configuration
lxc.net.0.type = macvlan
lxc.net.0.link = eth0
lxc.net.0.flags = up

Static IP vs DHCP

DHCP

The easiest way: let the container get an IP from the bridge. On an Ubuntu container with systemd-networkd:

Linux/etc/netplan/10-lxc.yaml inside the container
network:
  version: 2
  ethernets:
    eth0:
      dhcp4: true

Static IP

For a stable IP (needed by port forwarding or DNS), set a static IP inside the container. On the host, the LXC config can declare the network via lxc.net.0.ipv4.address:

LinuxStatic IP declaration in the container config
lxc.net.0.type = veth
lxc.net.0.link = lxcbr0
lxc.net.0.flags = up
lxc.net.0.ipv4.address = 10.0.3.10
lxc.net.0.ipv4.gateway = 10.0.3.1

Always keep consistency: the declaration in the LXC config and the configuration inside the container (netplan/NetworkManager) must point to the same IP.

Port Forwarding

To make a container reachable from outside the NAT, add DNAT on the host:

Forward host port 8080 to container port 80
sudo iptables -t nat -A PREROUTING -p tcp --dport 8080 -j DNAT --to-destination 10.0.3.10:80
sudo iptables -A FORWARD -p tcp -d 10.0.3.10 --dport 80 -j ACCEPT

Tip

For unprivileged containers, using lxc.net.0.ipv4.address with lxc.net.0.ipv4.gateway is the cleanest approach — LXC manages the routes and ARP automatically, without needing to touch netplan manually.

Quick Troubleshooting

  • Container without an IP: check ip addr inside the container and make sure lxc.net.0.flags = up is in the config.
  • Container can reach out but isn't reachable from outside: that's correct NAT behavior; add port forwarding.
  • macvlan can't ping the host: that's a macvlan design limitation; use veth if you need host-container communication.

Closing

Key takeaways:

  • lxcbr0 is the default NAT bridge (10.0.3.0/24) for container networking.
  • veth pairs connect the container's eth0 to the host bridge.
  • /etc/lxc/default.conf provides the network template; /etc/lxc/lxc-usernet grants veth quotas to unprivileged users.
  • Network modes: none, veth, macvlan, and phys.
  • IPs can be static (via the LXC config + guest configuration) or DHCP; external access via port forwarding.

In the next episode 7 we'll limit resources with cgroupslxc.cgroup2.memory.max and lxc.cgroup2.cpu.max in the container config, then verify with lxc-cgroup (legacy) and systemd-cgls on the host. This is how you make sure one container can't devour the host's entire RAM.

Learn LXC - Networking: bridge, veth & NAT | Learn LXC