How to run Docker on IPv6
When running Docker on an IPv6 only host it can be quite complicated to set it up properly. This blog post will help you to get your container running on an IPv6 only infrastructure and avoid the most common gotchas.
Prerequisites
- An IPv6 global prefix
- Root permissions on the host
A word of caution
The first thing to note is that running on an IPv6 only host, it means that only v6 networking is generally possible. So any communication running over v4 won’t be possible. For example lets try DNS resolution. If I try to get the IP for google.com on my IPv6 host I get the following response:
$ dig www.google.com
...
;; ANSWER SECTION:
www.google.com. 217 IN A 142.250.186.68
;; Query time: 0 msec
;; SERVER: 2a01:4ff:ff00::add:2#53(2a01:4ff:ff00::add:2) (UDP)
...One thing we can immediately see is, that the DNS server that answered our request has an IPv6 address, but what happens now if I tell dig to resolve the query via an IPv4 DNS server ?
$ dig @1.1.1.1 www.google.com
;; communications error to 1.1.1.1#53: timed outThe request timed out as we expected because our host is not capable of using the IPv4 protocol. The same behavior will be observable in our Docker containers unless we explicitly tell Docker to run in IPv6 mode. So, let’s get going!
Configure the docker daemon
To configure the daemon(assuming you use dockerd), we need to edit the daemon.json. On linux hosts you can find the
file under /etc/docker/daemon.json, if you’re running on another system consult
the documentation of docker engine. If the file does not exist simply create
it.
In order to fix the problem with the DNS resolution we need to tell the daemon to use IPv6 DNS servers. In this scenario I provided cloudflares DNS server. Additionally, we also instruct the daemon to enable v6 networking, as well as creating the default network with ULA addresses.
{
"IPv6": true,
"fixed-cidr-v6": "fd00:dead:beef::/48",
"dns": [
"2606:4700:4700::1111",
"2606:4700:4700::1001"
],
"ip6tables": true
}Retrieving the prefix
To make your container globally reachable, we have to create an IPv6 network based on the global prefix of your host. In
simplified terms, the global prefix is a 48-bit unique prefix that is used to route packets to your network. To find
your IPv6 global prefix, you can execute ip -6 addr show. You want to search for a similar entry like this:
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
...
inet6 2a01:4f8:1c0c:66b3::1/64 scope global noprefixroute
valid_lft forever preferred_lft forever
...As you can see, my hoster assigned me a /64 prefix. That means that the first 64 bits of the IPv6 address are fixed. So,
in my example, 2a01:04f8:1c0c:66b3 is the fixed part. If we would fully write out the IP, it would look like this 2a01: 04f8:1c0c:66b3:0000:0000:0000:0001.
Creating the IPv6 network
Now let’s choose a smaller subnet for our public network. For example lets choose a /80 subnet like
2a01:4f8:1c0c:66b3:1::/80 . This will ensure containers in that network will receive a globally routable address,
which means that those containers can be reached from the outside, unless blocked by a firewall or something similar.
$ docker network create --IPv6 --subnet 2a01:4f8:1c0c:66b3:1::/80 publicRun your container
Last but not the least run a container and attach it to our public network:
docker run --rm --network public -p [::]:8080:80 traefik/whoamiAnd if we now have a look at our container it has been assigned a globally routable IPv6 address. That we can reach from the internet.
$ docker inspect 31cb810f7c00
...
"Networks": {
"public": {
...
"IPv6Gateway": "2a01:4f8:1c0c:66b3:1::1",
"GlobalIPv6Address": "2a01:4f8:1c0c:66b3:1::3",
}
...