docker.recipes
Networking7 min read

IPv6 in Docker Compose

Enable IPv6 support in Docker for dual-stack networking, with configuration examples and common pitfalls.

01IPv6 and Docker: The Current State

Docker's IPv6 support has historically been limited, but it's improving. By default, Docker only uses IPv4 for container networking. Enabling IPv6 requires explicit configuration. **Why enable IPv6?** • Future-proofing your infrastructure • Some services/APIs prefer or require IPv6 • ISPs increasingly use IPv6 • Better end-to-end connectivity **Current limitations:** • Not enabled by default • Some features work differently than IPv4 • Documentation is sparse • NAT66 isn't well supported

If your ISP doesn't provide native IPv6, you can use a tunnel broker like Hurricane Electric (free) to get IPv6 connectivity.

02Enabling IPv6 in Docker Daemon

First, enable IPv6 at the Docker daemon level. This affects the default bridge network and allows creating IPv6-enabled networks. **Steps:** 1. Create or edit /etc/docker/daemon.json 2. Add IPv6 configuration 3. Restart Docker daemon You need a /64 or larger IPv6 subnet for Docker. If your ISP provides a /48 or /56, you can allocate a /64 from it.
1# /etc/docker/daemon.json
2{
3 "ipv6": true,
4 "fixed-cidr-v6": "fd00:dead:beef::/48",
5 "ip6tables": true,
6 "experimental": true
7}
8
9# Restart Docker
10sudo systemctl restart docker
11
12# Verify IPv6 is enabled
13docker network inspect bridge | grep -A 10 IPAM

Use a private IPv6 range (fd00::/8) for testing or if you don't have public IPv6 addresses. Public ranges require proper routing configuration.

03IPv6-Enabled Compose Networks

With IPv6 enabled in Docker, create dual-stack networks in your Compose files. Each network needs both IPv4 and IPv6 subnets configured. **Network configuration:** • enable_ipv6: true activates IPv6 • Define both ipv4 and ipv6 subnets under IPAM • Containers get addresses from both ranges
1services:
2 web:
3 image: nginx:latest
4 ports:
5 - "80:80"
6 - "443:443"
7 networks:
8 - dual_stack
9
10networks:
11 dual_stack:
12 enable_ipv6: true
13 ipam:
14 config:
15 - subnet: 172.28.0.0/16 # IPv4
16 - subnet: fd00:db8:1::/64 # IPv6

04Verifying IPv6 Connectivity

After configuration, verify that containers have IPv6 addresses and can communicate over IPv6. **What to check:** • Container has IPv6 address • Can ping IPv6 addresses • Can resolve AAAA records • Services bind to IPv6
1# Check container's IPv6 address
2docker inspect mycontainer --format='{{range .NetworkSettings.Networks}}IPv6: {{.GlobalIPv6Address}}{{end}}'
3
4# Test IPv6 connectivity from inside container
5docker exec mycontainer ping6 -c 3 ipv6.google.com
6
7# Check DNS resolution for AAAA records
8docker exec mycontainer nslookup -type=AAAA google.com
9
10# See all addresses
11docker exec mycontainer ip addr show
12
13# Test from host
14curl -6 http://[fd00:db8:1::2]:80/

Use test-ipv6.com from inside a container (with a browser or curl) to verify full IPv6 connectivity.

05Port Binding with IPv6

When exposing ports, you can bind to specific IPv4 or IPv6 addresses, or both. By default, Docker binds to IPv4 only. **Binding options:** • ports: ["80:80"] - IPv4 only (0.0.0.0) • ports: ["[::]:80:80"] - IPv6 only • ports: ["0.0.0.0:80:80", "[::]:80:80"] - Both explicitly
1services:
2 # IPv4 only (default)
3 web-v4:
4 image: nginx:latest
5 ports:
6 - "80:80"
7
8 # IPv6 only
9 web-v6:
10 image: nginx:latest
11 ports:
12 - "[::]:8080:80"
13
14 # Dual-stack (both IPv4 and IPv6)
15 web-dual:
16 image: nginx:latest
17 ports:
18 - "0.0.0.0:8081:80"
19 - "[::]:8081:80"
20
21 # Specific addresses
22 web-specific:
23 image: nginx:latest
24 ports:
25 - "192.168.1.50:8082:80"
26 - "[fd00::1]:8082:80"

When binding to [::], ensure your firewall is configured for IPv6. Many firewalls only filter IPv4 by default.

06IPv6 NAT and Public Addresses

Unlike IPv4, IPv6 was designed without NAT—every device gets a public address. This changes how you think about container networking. **Options:** **1. Private addresses (NAT66)** Use fd00::/8 range with ip6tables masquerading. Similar to IPv4 NAT. **2. Public addresses** Containers get routable IPv6 addresses. More "correct" but requires firewall configuration. **3. Proxy/tunnel** Use a reverse proxy or Cloudflare Tunnel for IPv6 access.
1# Option 1: NAT66 with private addresses
2# Requires: daemon.json with "ip6tables": true
3networks:
4 nat66:
5 enable_ipv6: true
6 ipam:
7 config:
8 - subnet: 172.20.0.0/16
9 - subnet: fd00:cafe::/64 # Private, NATed
10
11# Option 2: Public IPv6 (if you have a routed /64)
12networks:
13 public_v6:
14 enable_ipv6: true
15 ipam:
16 config:
17 - subnet: 172.21.0.0/16
18 - subnet: 2001:db8:abcd::/64 # Your public range
19 driver_opts:
20 com.docker.network.bridge.enable_ip_masquerade: "false"

For most self-hosters, NAT66 with private addresses is the easiest approach. It works just like IPv4 NAT.

07Troubleshooting IPv6 Issues

IPv6 issues are often caused by missing configuration, firewall rules, or routing problems. **Common issues:** **No IPv6 address assigned:** • Check daemon.json configuration • Verify network has enable_ipv6: true • Restart Docker after config changes **Can't reach external IPv6:** • Check host has IPv6 connectivity • Verify ip6tables rules • Check ISP/router IPv6 support **Service not accessible via IPv6:** • Verify port binding includes [::]: • Check firewall allows IPv6 traffic • Confirm service listens on IPv6
1# Check Docker's IPv6 configuration
2docker info | grep -i ipv6
3
4# Check host IPv6 connectivity
5ping6 -c 3 ipv6.google.com
6
7# View ip6tables rules
8sudo ip6tables -L -n
9
10# Check container's IPv6 routing
11docker exec mycontainer ip -6 route
12
13# Debug network configuration
14docker network inspect mynetwork | jq '.[0].IPAM'
15
16# Test from another IPv6 host
17curl -6 -v http://[your-ipv6-address]:80/
18
19# Check if service is listening on IPv6
20docker exec mycontainer ss -tlnp | grep -E '::.*:80'