When people first start learning container networking, terms like network namespaces, veth pairs, and routing tables can sound complicated.
But under the hood, the idea is surprisingly simple.
Recently, I spent some time experimenting with Linux networking directly from the terminal to better understand how containers communicate internally. In this post, I’ll walk through the process step by step and explain what each command actually does instead of only showing commands to copy and paste.
Understanding the Linux Network Stack
Linux networking is built around network interfaces.
A network interface is simply a communication point between your machine and a network.
To see all available interfaces:
ip link show
You will usually notice interfaces like:
-
lo→ Loopback interface -
eth0→ Main Ethernet interface
The ip command comes from the iproute2 package and is one of the most important networking tools in Linux.
The Loopback Interface
The loopback interface (lo) is a special internal interface that allows a machine to communicate with itself.
Check its details:
ifconfig lo
You will notice it usually has the IP address:
127.0.0.1
This is commonly called localhost.
Applications use the loopback interface for internal communication without sending traffic outside the machine.
Understanding Routes
Whenever Linux sends a packet, it first checks the routing table to decide where the packet should go.
You can inspect the routing table using:
ip route show
Typical output looks something like:
default via 192.168.1.1 dev eth0
192.168.1.0/24 dev eth0 proto kernel scope link
This tells Linux:
- Traffic for the local network should go through
eth0 - Everything else should go to the default gateway (
192.168.1.1)
Without routes, Linux would not know where to send packets.
Checking Firewall Rules
Linux provides packet filtering through iptables.
View current rules:
iptables -L
iptables is commonly used for:
- Firewalls
- Traffic filtering
- NAT
- Port forwarding
Container technologies also use iptables extensively behind the scenes.
Creating an Isolated Network Namespace
A network namespace creates a completely isolated networking environment.
Each namespace has its own:
- Interfaces
- Routing table
- Firewall rules
- ARP table
This is one of the core technologies used by containers.
Create a namespace:
sudo ip netns add red
Now list available namespaces:
sudo ip netns list
You should see:
red
At this point, the namespace exists, but it is isolated and cannot communicate with anything yet.
Entering the Namespace
Run a shell inside the namespace:
sudo ip netns exec red bash
Now any networking command you run operates inside the isolated namespace instead of the host machine.
Check interfaces:
ip link show
You will mostly see only the loopback interface because no external network connection exists yet.
Creating a Virtual Ethernet Pair
To connect the namespace with the host machine, we need a communication channel.
Linux provides this using a virtual Ethernet pair (veth).
Create the pair:
sudo ip link add veth-red type veth peer name veth-host
This creates two linked virtual interfaces:
veth-redveth-host
Think of them like two ends of a physical Ethernet cable.
Anything entering one side instantly appears on the other side.
Initially, both interfaces exist inside the host namespace.
Moving One Interface into the Namespace
Now move one side into the red namespace:
sudo ip link set veth-red netns red
After this:
-
veth-redlives inside the namespace -
veth-hostremains on the host
Now the namespace has one end of the virtual cable.
Assigning IP Addresses
Network interfaces need IP addresses to communicate.
Configure the namespace side:
sudo ip netns exec red ip addr add 192.168.1.1/24 dev veth-red
This assigns:
192.168.1.1
to veth-red.
Now enable the interface:
sudo ip netns exec red ip link set veth-red up
Interfaces are disabled by default after creation, so bringing them "up" is necessary.
Now configure the host side:
sudo ip addr add 192.168.1.2/24 dev veth-host
sudo ip link set veth-host up
At this point:
- Host side →
192.168.1.2 - Namespace side →
192.168.1.1
Both devices are now connected through the virtual Ethernet pair.
Why We Need to Add a Route
This is one of the most important parts of the setup.
Add the route:
sudo ip route add 192.168.1.1 dev veth-host
But why is this necessary?
When Linux sends packets, it checks the routing table first.
Without this route, Linux has no idea where 192.168.1.1 exists.
Even though the interface physically exists, the kernel still needs routing information.
By adding the route, we are telling Linux:
"To reach 192.168.1.1, send packets through veth-host."
Without this step:
- Ping would fail
- Packets would never leave correctly
- Linux would not know which interface to use
Routing tables are essentially maps for packet delivery.
Testing Connectivity
Now test communication from the host:
ping 192.168.1.1 -c 3
You should receive replies successfully.
Now enter the namespace:
sudo ip netns exec red bash
And ping the host:
ping 192.168.1.2 -c 3
At this point, both sides can communicate through the virtual Ethernet cable.
Why This Matters in Real Systems
This exact concept is heavily used in:
- Docker
- Kubernetes
- Podman
- Linux containers
- Cloud infrastructure
When a container gets its own IP address, Linux namespaces and veth pairs are often involved behind the scenes.
Understanding this manually makes container networking much easier to understand later.
Final Thoughts
Before learning this, container networking felt almost magical to me.
But once you experiment directly with namespaces, routes, and veth devices, the whole system starts making much more sense.
Linux networking is incredibly modular and powerful, and understanding these building blocks gives a much deeper understanding of how modern infrastructure actually works.
Top comments (0)