How to Configure IPv6 using SLAAC for Docker containers
I recently tore down my home network and rebuilt it properly, everything worked well enough. The problem showed up when I started thinking…
How to Configure IPv6 using SLAAC for Docker containers
I recently tore down my home network and rebuilt it properly, everything worked well enough. The problem showed up when I started thinking about BitTorrent applications.
BT applications work much better when they have proper IPv6 connectivity and fewer NAT-related headaches. The trouble is that my home IPv6 address is only available through SLAAC. I do not get a neat DHCPv6-style address that I can easily assign by hand inside a container. The router advertises the prefix, devices listen for Router Advertisements, and then they build their own IPv6 addresses from there.
That is fine for ordinary devices on the LAN but not when Docker enters the room. I also did not want to solve this by using Docker’s host network mode. Host mode would have been the easy way out, but it also felt like giving the container far too much trust. If that container gets compromised, I do not want it standing directly inside the host network with more access than it needs. A home lab does not need enterprise paranoia, but it does need some basic walls. Otherwise the whole VLAN design becomes decorative.
So the goal was simple, keep the BT container inside its own VLAN, allow it to receive IPv6 through SLAAC, and avoid Docker host networking.
That meant I needed proper NDP behavior inside that VLAN. The container had to appear on the Layer 2 segment in a way that allowed it to receive Router Advertisements from the router and configure its own IPv6 address automatically. Docker’s normal bridge networking is not great for this. In regular Docker IPv6 mode, you usually end up dealing with static IPv6 addresses, manual subnets, and a setup that feels like it is fighting the way a normal home IPv6 network actually works.
The better option here is Docker’s ipvlan driver. In ipvlan L2 mode, the container can sit much closer to the real VLAN network while still avoiding host mode. The parent interface is the VLAN interface on the host, and the container gets attached to that Layer 2 network. That gives the container a much better chance of receiving IPv6 Router Advertisements and configuring itself through SLAAC.
First, I created an ipvlan network for VLAN 103:
docker network create -d ipvlan \
--subnet=10.103.0.0/24 \
--ip-range=10.103.0.48/28 \
--gateway=10.103.0.1 \
--ipv6 \
--subnet=fd00:103::/64 \
-o parent=enp1s0.103 \
-o ipvlan_mode=l2 \
lan1_vlan_103
The important part is the parent interface:
-o parent=enp1s0.103
That is the VLAN interface on the Docker host. In my case, VLAN 103 is already set up on the host as enp1s0.103, so Docker can attach the container network to that VLAN instead of placing the container behind the usual Docker bridge.
The other important part is this:
-o ipvlan_mode=l2
This is what makes the container behave more like it belongs to that Layer 2 network. It is still not the same as host networking, but it is much closer to the actual VLAN than the default Docker bridge. For this use case, that matters because IPv6 SLAAC depends on Router Advertisements reaching the container.
After creating the network, the next step was to attach the container to it. The catch is that the container also needs to accept Router Advertisements. By default, Linux can be picky here, especially when forwarding or container networking is involved. So I added these sysctl parameters:
--sysctl net.ipv6.conf.all.accept_ra=2
--sysctl net.ipv6.conf.eth0.accept_ra=2
The value 2 is the useful part. It tells the system to accept Router Advertisements even in cases where it might otherwise ignore them. Without this, the container may join the network but still fail to pick up the IPv6 address through SLAAC, which is exactly the kind of half-working network problem that wastes an afternoon.
After that, I tested IPv6 from inside the container:
docker exec -it uptime-kuma ping6 240e:f7:e01f:f1::30
And it worked:
PING 240e:f7:e01f:f1::30(240e:f7:e01f:f1::30) 56 data bytes
64 bytes from 240e:f7:e01f:f1::30: icmp_seq=2 ttl=56 time=12.0 ms
64 bytes from 240e:f7:e01f:f1::30: icmp_seq=3 ttl=56 time=11.5 ms
64 bytes from 240e:f7:e01f:f1::30: icmp_seq=4 ttl=56 time=12.9 ms
64 bytes from 240e:f7:e01f:f1::30: icmp_seq=5 ttl=56 time=11.4 ms
64 bytes from 240e:f7:e01f:f1::30: icmp_seq=6 ttl=56 time=11.7 ms
64 bytes from 240e:f7:e01f:f1::30: icmp_seq=7 ttl=56 time=11.8 ms
64 bytes from 240e:f7:e01f:f1::30: icmp_seq=8 ttl=56 time=11.8 ms
--- 240e:f7:e01f:f1::30 ping statistics ---
8 packets transmitted, 7 received, 12.5% packet loss, time 28ms
rtt min/avg/max/mdev = 11.405/11.876/12.895/0.472 ms
There was a little packet loss in that test, but the important part was that IPv6 connectivity was working from inside the container. The container was no longer trapped behind Docker’s usual IPv6 weirdness, and I did not have to put it into host mode to make it reachable.
The final confirmation came from the router itself. The router showed the assigned IPv6 address under VLAN 103:
31 D 240e:xxx:xxxxxx:a500:11b:f035 C0:25:A5:1B:F0:35 vlan103-bt main
That was the part I actually wanted to see. The container had joined the VLAN, received an IPv6 address through SLAAC, and appeared to the router like a normal device on that segment.
메타데이터
- post_id
- ff0ec0d439d6
- slug
- how-to-configure-ipv6-using-slaac-for-docker-containers-ff0ec0d439d6
- url
- https://medium.com/@u.mair/how-to-configure-ipv6-using-slaac-for-docker-containers-ff0ec0d439d6
- canonical_url
- https://medium.com/@u.mair/how-to-configure-ipv6-using-slaac-for-docker-containers-ff0ec0d439d6
- author_url
- https://medium.com/@u.mair
- status
- ok
- fetched_at
- 2026-06-12 07:40:50