Explains Linux network namespaces, connecting them with veth pairs and bridges, and enabling host and external access using routing, IP forwarding, NAT and port forwarding
In this lesson we introduce Linux network namespaces — the fundamental building blocks that provide network isolation for containers (for example, Docker containers — see Docker Training Course for the Absolute Beginner). We start with the host and progressively build namespaces, connect them with virtual links, and enable external access using routing and NAT.If your host is a house, namespaces are the rooms you assign to each child. A room provides privacy: each child sees only what is inside their room and thinks they are the only person in the house. The parent (the host) still has visibility into all rooms and the rest of the house.
When you create a container, the runtime creates a network namespace for it. Inside that namespace the container sees only the processes and network devices assigned to it and behaves as if it runs on a private host. The underlying host still sees all processes and interfaces.Example: process list inside a container versus on the hostInside the container:
$ ps auxUSER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMANDroot 1 0.0 0.0 4528 828 ? Ss 03:06 0:00 nginx
Same process from the host (PID differs and appears among other host processes):
Network namespaces also separate interfaces, ARP tables, and routing tables. The host keeps its own network stack (interfaces, routes, ARP), while each namespace can have independent virtual interfaces, ARP entries, and routing.
Network namespaces, veth pairs, moving interfaces, and iptables changes require root privileges. Prepend sudo to commands or run as root.
# Create two namespacesip netns add redip netns add blue# List namespacesip netns list# View host interfacesip link
Run commands inside a namespace using ip netns exec or ip -n:
ip netns exec red ip link# or equivalentlyip -n red link
Typical output:
# On the host$ ip link1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 ...2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 ...# Inside namespace 'red'$ ip -n red link1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 ...
Note how red only shows the loopback interface; it cannot see the host’s eth0. Each namespace maintains its own ARP (arp -n) and routing table (ip route).
Virtual Ethernet pairs (veth) — connect two namespaces
A veth pair acts like a virtual cable with two ends. To directly connect two namespaces:
# Create a veth pair and move each end to a different namespaceip link add veth-red type veth peer name veth-blueip link set veth-red netns redip link set veth-blue netns blue
Assign IPs and bring interfaces up inside each namespace:
ip -n red addr add 192.168.15.1/24 dev veth-redip -n blue addr add 192.168.15.2/24 dev veth-blueip -n red link set veth-red upip -n blue link set veth-blue up
Test connectivity:
ip netns exec red ping -c 1 192.168.15.2
Check ARP tables inside namespaces:
ip netns exec red arp -n# Address HWtype HWaddress Flags Ifaceip netns exec blue arp -n# Address HWtype HWaddress Flags Iface# 192.168.15.1 ether de:ad:be:ef:00:01 C veth-blue
The host ARP table does not include these entries because the veth interfaces are private to namespaces.
From point-to-point to a virtual network: Linux bridge
Direct veth pairs are fine for two endpoints. To connect multiple namespaces to the same Layer 2 network, create a Linux bridge on the host and attach each namespace via a veth peer.
Create and enable the bridge on the host:
ip link add v-net-0 type bridgeip link set dev v-net-0 upip link # v-net-0 appears as an interface on the host
If you created a direct veth pair earlier and want to switch to a bridged network, delete the direct pair (note: deleting one end removes both):
ip link delete veth-red
Create veth pairs for each namespace where one side stays in the namespace and the other attaches to the bridge:
ip link add veth-red type veth peer name veth-red-brip link add veth-blue type veth peer name veth-blue-br# Move namespace-side interfaces into namespacesip link set veth-red netns redip link set veth-blue netns blue# Attach host-side peers to the bridge and bring them upip link set veth-red-br master v-net-0ip link set veth-blue-br master v-net-0ip link set veth-red-br upip link set veth-blue-br up
Inside each namespace, assign IPs and bring interfaces up:
ip -n red addr add 192.168.15.1/24 dev veth-redip -n blue addr add 192.168.15.2/24 dev veth-blueip -n red link set veth-red upip -n blue link set veth-blue up
All namespaces connected to v-net-0 can communicate at Layer 2 through the bridge.
Allowing namespaces to reach external networks (routing + NAT)
Namespaces on the bridge can talk to each other and the host, but they cannot reach the external LAN or internet until you provide routing and NAT.Example scenario:
Host external interface: eth0 = 192.168.1.2
Bridge interface: v-net-0 = 192.168.15.5
Namespace blue = 192.168.15.2
Add a route inside the namespace so it uses the host as a gateway:
ip netns exec blue ip route add 192.168.1.0/24 via 192.168.15.5# Or set a default route:ip netns exec blue ip route add default via 192.168.15.5
Enable IP forwarding on the host:
sysctl -w net.ipv4.ip_forward=1# To persist, add to /etc/sysctl.conf:# net.ipv4.ip_forward = 1
Set up NAT (masquerading) so replies from the external network are correctly routed back through the host:
iptables -t nat -A POSTROUTING -s 192.168.15.0/24 -j MASQUERADE# Optional: limit to a specific outgoing interface:# iptables -t nat -A POSTROUTING -s 192.168.15.0/24 -o eth0 -j MASQUERADE
Exposing a service running in a namespace to the LAN
Two common approaches to make a namespace service reachable from other hosts on the LAN:
Add routing on remote hosts so they know the private network is reachable via your host (often impractical).
Use port forwarding (DNAT) on the host to map a host port to the namespace IP:port.
Example: forward host TCP port 80 to blue namespace port 80 and also apply masquerade:
# Forward incoming port 80 to 192.168.15.2:80iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to-destination 192.168.15.2:80# Ensure outbound packets are masqueraded (so return traffic is routed correctly)iptables -t nat -A POSTROUTING -s 192.168.15.0/24 -j MASQUERADE
Also ensure the filter table allows forwarding and the FORWARD chain accepts the forwarded packets as needed.
Deleting one end of a veth pair removes both ends. Be careful when deleting interfaces and ensure important iptables rules are saved if you want persistence across reboots.
That’s it for this lesson — you now know how to create network namespaces, connect them with veth pairs and bridges, enable host access, and expose namespace services to the outside world.