Docker - network sharing

One of the lesser known options of networking in Docker is sharing of network namespaces. In a bit clearer terms - a situation where 2 (or more) containers from a user point of view use the same “virtual” network interface.

It’s not surprising that this option is not widely used - most of the times ability to bind any port in the container to a host port covers intended use case.

So when this becomes useful? Personally I’ve come across this when I’ve been working with a networks that used Tailscale or Netbird. I really needed a service to use the same network interface that Tailscale / Netbird did and at the same time I couldn’t simply do network_mode: host as the ports I’ve needed were already taken.

Macvlan with additional options that are available through network_mode saved the day - as they allow specifying the name of a service / container which network interface we’d like to “borrow”. Simply write it down instead of host (which you’re probably more used to):

network_mode: service:<SERVICE_NAME>

Here’s a simple example to test it (dropping macvlan settings to make it clearer):

networks:
  shared-net:

services:
  nginx1:
    container_name: nginx1
    image: nginx:latest
    environment:
      - NGINX_PORT=6080
      - SERVER_NAME=nginx1
    networks:
      - shared-net
    volumes:
      - ./nginx-conf.template:/etc/nginx/conf.d/nginx-conf.template
    command: /bin/bash -c "envsubst < /etc/nginx/conf.d/nginx-conf.template > /etc/nginx/conf.d/default.conf && nginx -g 'daemon off;'"

  nginx2:
    container_name: nginx2
    image: nginx:latest
    environment:
      - NGINX_PORT=7080
      - SERVER_NAME=nginx2
    network_mode: service:nginx1
    volumes:
      - ./nginx-conf.template:/etc/nginx/conf.d/nginx-conf.template
    command: /bin/bash -c "envsubst < /etc/nginx/conf.d/nginx-conf.template > /etc/nginx/conf.d/default.conf && nginx -g 'daemon off;'"

In this case nginx2 container uses nginx1 service network interface.

To run this example an additional template file for nginx config is needed. It can look like this:

server {
    listen       ${NGINX_PORT};
    listen  [::]:${NGINX_PORT};
    server_name  ${SERVER_NAME};

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }

}

After starting the containers via docker compose up -d you can open bash in one of them by:

docker container exec -it nginx2 bash

Then use curl to check whether you have nginx on localhost port 6080 (from nginx1) and 7080 (from nginx2):

curl http://localhost:6080
curl http://localhost:7080

Actually inspecting the config with docker container inspect on the host system gives even better info:

docker container inspect nginx2

“NetworkMode” and “NetworkSettings” section of the output are work checking out. In my case the first one contained:

"NetworkMode": "container:cd15bb65e8af98ab0a383269277b7e3710b3d85189869742202884cce4f46f66"

While the second one had empty / null entries - which more definitively illustrates that nginx2 does not have its own network interface.

https://docs.docker.com/reference/compose-file/services/

https://docs.docker.com/reference/compose-file/services/#network_mode

Image(s):

Alina Grubnyak @Unsplash

Przemek