Enable IPv6 for Docker (Update: and Docker Compose Containers!)

(This might be applicable to Manjaro on other architectures, too, but I know for sure it's applicable to ARM. Please move this post if it's in the wrong place.)

Continuing my adventures in Docker setup, started here: Pi Hole with/without Docker?

Source, with much more information and explanation: https://medium.com/@skleeschulte/how-to-enable-ipv6-for-docker-containers-on-ubuntu-18-04-c68394a219a2

My network runs both IPv4 and IPv6, and I get a public v6 address from my ISP with no issues. Since I wanted to set up docker specifically to run pi-hole, and I am capable of making IPv6 DNS resolution requests, I wanted to get v6 enabled in Docker.

Docker provides these instructions: https://docs.docker.com/config/daemon/ipv6/

A few points:

  • The instructions indicate a key-value pair should be added to /etc/docker/daemon.json to enable IPv6. If this file does not exist, do not create it. Docker can take instructions on startup either from a daemon.json file or via command line flags. Trying to give it both can possibly lead to strange errors/docker failing to start, etc.

    • Update: The docker docs (https://docs.docker.com/config/daemon/) say it's fine to use both as long as they don't overlap, and for reasons discussed below I think using both is probably ideal. My earlier statement came from some anecdotal reports of problems caused by mixing them. The key is not to use the command line and the .json file to set the same flags. Sorry for any confusion on this point.
  • When I installed docker from pacman, it did not create the daemon.json file, so instead we're using command line flags.

  • The command line flags are passed to dockerd in docker's system-wide .service file's ExecStart command, at /usr/lib/systemd/system/docker.service.

  • Important: when you update docker through pacman, the update will erase your changes to the comand line flags in the docker.service file. You will need to put them back using the instructions below. (This is, I think, why it is considered preferable to use the daemon.json file to manage your settings. It should persist after package upgrades.
    Option 1: Editing the .service file:

    • :imp: Critical: :imp: either back up this file or comment out the existing ExecStart command before adding a new ExecStart. If you do not input a valid replacement, dockerd will fail to start. Make sure you can bring it back.
    • Edit the ExecStart command to look like this: ExecStart=/usr/bin/dockerd -H fd:// --ipv6 --fixed-cidr-v6 "fd00::/80"
    • Note 1: --ipv6 enables IPv6.
    • Note 2: --fixed-cidr-v6 gives docker an, in this case, locally accessible subnet of size /80 IPv6 addresses to play with. Without the subnet, dockerd will crash on start with a mildly spectacular error. You can give it a pool of globally accessible IPv6 addresses if your ISP issues you one of those, but this punches a massive hole in docker's isolation wall, as your containers are now using addresses on the public internet.
    • Close and save the file.
  • Option 2: Creating a daemon.json file:

    • run sudo nano /etc/docker/daemon.json to create a blank config file named daemon.json
    • Add the following key-value pairs to the file, exactly as written, then save and close the file:

{
"ipv6": true,
"fixed-cidr-v6": "fd00::/80"
}

  • run systemctl daemon-reload
  • run systemctl restart docker.service
  • If the service successfully restarts, IPv6 support is now enabled. If it doesn't, either you or I made a typo somewhere.

Inspecting your docker's bridge network should now look something like this. Notice the IPv6 bits:
image

  1. If you have an IPv6 address, but you don't see an IPv6 gateway: reboot the machine and check again.
  2. The subnet we assigned led to auto-creation of a default gateway on our local network. Without further configuration (see below), the bridge network's v6 topography cannot access the internet. The v4 gateway, by default, can. I'm still investigating how best to fix this.

Final Step: Figure out the best way to bug the docker developers to please make this simpler. It is my understanding that this not happening automatically just by using the enable IPv6 flag is actually a bug. Consider: IPv4 works automatically.

But wait! Sadly, that's not the end of it. While your docker containers can now get IPv6 addresses and do IPv6 things, they cannot access anything outside your local network, since you gave them a local subnet. To let them outside, you'll need to "enable NAT for the private Docker subnet on the host."

More critically, I'm not sure this command works on Manjaro yet, and even the vague idea of mixing NAT with IPv6 is giving me hives. Perhaps @Strit or @Darksky or @linux-aarhus might know for sure if it's safe to run this command on Manjaro.

EDIT 4 (What is time? When am I?)
I've blanked out all my attempts to helpfully summarize how to do the IPv6 routing with pure docker. I cannot get my head around that. All the information I had originally posted is up in the link at the top, but I'm afraid if I try to talk about it, I'll just do more harm than good.

New Strategy: Enable IPv6 routing on a per-container basis using docker-compose for new containers. This has the advantage of being much easier to understand. Also, it actually worked when I tried it, which is a plus.

Source: https://gist.github.com/tntwist/a6183bbf736d7d652d1fc01a32c9c19a
(No, at this point I'm not sure how I found this. I actually got lost on Google and stumbled into a bugtracker report where someone happened to volunteer their working configuration.)

That's an example of a docker-compose setup for pi-hole, using cloudflared to provide DNS over HTTPS, and supporting IPv6 ad block. I hope. Still testing, but the service came up and the docker network looks good.

For existing containers: If you've already got a docker container you want to add IPv6 to, there's a way to do that. Here's an example, again using pihole.
https://www.neilgrogan.com/docker-compose-ipv6/

Good luck!

3 Likes

Hi, topic was moved to #technical-issues-and-assistance:tutorials
regards

1 Like

Thanks!

I've updated the instructions with an option to use a daemon.json persistent config file. This will keep updates to the docker package using pacman -S from resetting things to default and disabling IPv6.

I also added some additional explanation about why the IPv6 version of the bridge network can't actually get to the internet out of the box.

Just use that: http://aur.archlinux.org/packages/docker-ipv6nat

You can also use userns with this because you use it with this package as host base and not container base

@Elrondo46 , thanks for the link to ipv6nat. That's definitely one way to go, and makes things super-easy.

Could you explain/point to an explanation of what userns is and why it would be helpful for this?

I'm still working on teaching myself to do it the manual way (with direct manipulation of ip6tables. I think I can come up with a way to make that work that would be fairly easy to manage and would not require using ipv6nat--I'd prefer not to rely on a docker image that might not always be maintained going forward, or that might otherwise break after a future docker update.

Fingers crossed the docker developers figure out a way to make v6 easier to use out of the box, or at least update the docs--especially since pi-hole, a DNS server, is often used as a great example of a first Docker project.

In userns, the docker not runs as root, in the container you have just a fake root. It's more secure.
Example: Run stack nginx+pgsql, the true running user is dockremap and dockremap has a subuid of 100000. If you run bash and enter inside the container, root of it has no root rights inside the real system.

https://wiki.archlinux.org/index.php/docker#User_namespace_remapping

Sorry for my english, I'm french

Forum kindly sponsored by