Build a TCP/IP Stack from Scratch · Module 01

Step-by-Step: Build and Run the Containers

Step-by-step: Build and run the containers

Time to make packets fly. We'll create two super-simple containers on a private Docker network and prove they can see each other.

4.1 Create the Dockerfiles and compose file

In lab/client.Dockerfile:

FROM debian:stable-slim
RUN apt-get update && apt-get install -y \
 iproute2 iputils-ping tcpdump curl arping \
 && rm -rf /var/lib/apt/lists/*
CMD ["sleep", "infinity"]

In lab/stack.Dockerfile (empty "stack" for now — later this will run your code):

FROM debian:stable-slim
RUN apt-get update && apt-get install -y \
 iproute2 iputils-ping tcpdump arping \
 && rm -rf /var/lib/apt/lists/*
CMD ["sleep", "infinity"]

In lab/docker-compose.yml:

version: "3.9"
networks:
 labnet:
 driver: bridge
 ipam:
 config:
 - subnet: 10.10.0.0/24
services:
 client:
 build:
 context: .
 dockerfile: client.Dockerfile
 command: sleep infinity
 networks:
 labnet:
 ipv4_address: 10.10.0.3
 stack:
 build:
 context: .
 dockerfile: stack.Dockerfile
 command: sleep infinity
 networks:
 labnet:
 ipv4_address: 10.10.0.4

Folder reminder: all three files live in netstack/lab/.

4.2 Bring the lab up

From netstack/lab/:

 lab git:(main) docker compose up -d --build

List containers:

 lab git:(main) docker compose ps

You should see client and stack running:

 lab git:(main) docker compose ps 
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
lab-client-1 lab-client "sleep infinity" client 24 seconds ago Up 24 seconds 
lab-stack-1 lab-stack "sleep infinity" stack 24 seconds ago Up 24 seconds 

4.3 Verify basic connectivity

Check IPs:

 lab git:(main) docker compose exec client ip -brief addr
docker compose exec stack ip -brief addr
lo UNKNOWN 127.0.0.1/8 ::1/128 
eth0@if16 UP 10.10.0.3/24 
lo UNKNOWN 127.0.0.1/8 ::1/128 
eth0@if18 UP 10.10.0.4/24 
 lab git:(main) 

Ping from client → stack:

 lab git:(main) docker compose exec client ping -c 3 10.10.0.4
PING 10.10.0.4 (10.10.0.4) 56(84) bytes of data.
64 bytes from 10.10.0.4: icmp_seq=1 ttl=64 time=0.198 ms
64 bytes from 10.10.0.4: icmp_seq=2 ttl=64 time=0.141 ms
64 bytes from 10.10.0.4: icmp_seq=3 ttl=64 time=0.164 ms
 
--- 10.10.0.4 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2058ms
rtt min/avg/max/mdev = 0.141/0.167/0.198/0.023 ms

You should get replies (64 bytes from 10.10.0.4 ...). If not, we'll troubleshoot in the next section; for most setups this "just works."

That's your tiny LAN online: two hosts on 10.10.0.0/24 talking over Docker's virtual switch. context: . dockerfile: client.Dockerfile command: sleep infinity networks: labnet: ipv4_address: 10.10.0.3

stack: build: context: . dockerfile: stack.Dockerfile command: sleep infinity networks: labnet: ipv4_address: 10.10.0.4


## 4.2 Bring the Lab Up

From `netstack/lab/`:

```bash
docker compose up -d --build

List containers:

docker compose ps

You should see:

NAME IMAGE COMMAND SERVICE CREATED STATUS 
lab-client-1 lab-client "sleep infinity" client 24 seconds ago Up 24 seconds 
lab-stack-1 lab-stack "sleep infinity" stack 24 seconds ago Up 24 seconds

4.3 Verify Basic Connectivity

Check IPs:

docker compose exec client ip -brief addr
docker compose exec stack ip -brief addr

Expected output:

lo UNKNOWN 127.0.0.1/8 ::1/128 
eth0@if16 UP 10.10.0.3/24 

lo UNKNOWN 127.0.0.1/8 ::1/128 
eth0@if18 UP 10.10.0.4/24

Ping from client → stack:

docker compose exec client ping -c 3 10.10.0.4

Expected output:

PING 10.10.0.4 (10.10.0.4) 56(84) bytes of data.
64 bytes from 10.10.0.4: icmp_seq=1 ttl=64 time=0.198 ms
64 bytes from 10.10.0.4: icmp_seq=2 ttl=64 time=0.141 ms
64 bytes from 10.10.0.4: icmp_seq=3 ttl=64 time=0.164 ms

Success: You should get replies (64 bytes from 10.10.0.4...). That's your tiny LAN online: two hosts on 10.10.0.0/24 talking over Docker's virtual switch.