Michael Stapelberg

Docker and IPv6

My use-case is seemingly very simple: I want to run a webserver in a Docker
container, and it should be reachable via IPv4 and IPv6. The webserver has
multiple virtual hosts, some of which just serve static files, while others
proxy to, say, a Grafana instance, which is also running
in a Docker container.

This article walks through the required steps, which are a bit cumbersome to
puzzle together from Docker’s official documentation.

Enabling IPv6 in Docker

The Docker daemon defaults to IPv4-only. To enable IPv6, create the
configuration file /etc/docker/daemon.json with the following content:

{
"ipv6": true,
"fixed-cidr-v6": "2001:db8:13b:330:ffff::/80"
}

After restarting the Docker daemon, containers will now get IPv6 addresses based
on their MAC address, which is picked sequentially from the range
02:42:ac:11:00:00 to 02:42:ac:11:ff:ff. That is, the first container you
start will use the IPv6 address 2001:db8:13b:330:ffff:0242:ac11:0002.

Publishing ports and remote addresses

When publishing port 80 of a webserver, notice the remote address when accessing
the port via IPv4 and IPv6:

Some people resort to using Docker’s host network option, but that’s not a
good solution: your container will not be able to talk to other containers by
name anymore, so you will need lots of static, host-specific configuration.

A better solution is to only publish the port via IPv4 and connect to the
container’s IPv6 address directly:

% docker run --publish 203.0.113.1:80:80 --name nginx nginx

You can obtain the container’s IPv6 address using:

% docker inspect -f '{{.NetworkSettings.GlobalIPv6Address}}' nginx

Static IPv6 addresses

Above, I explained that we need to use the container’s IPv6 address directly,
and that the address is derived from the MAC address, which is chosen
sequentially at container start time.

Having addresses depend on the order in which containers come up isn’t a robust
solution for my simple setup, where I want to statically configure a DNS record.

Docker allows specifying an IPv6 address, but only when you’re using a user-defined bridge network with an IPv6 subnet carved out for the network, like so: