# Setting Up WireGuard with IPv6 Support in Docker on Ubuntu

**URL:** <https://forum.hhf.technology/t/setting-up-wireguard-with-ipv6-support-in-docker-on-ubuntu/3894>\
**Category:** Networking\
**Tags:** wireguard, ipv6, ubuntu\
**Created:** [October 19, 2025, 6:18pm UTC](https://forum.hhf.technology/t/setting-up-wireguard-with-ipv6-support-in-docker-on-ubuntu/3894 "2025-10-19T18:18:31Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![hhf.technoloy](https://forum.hhf.technology/user_avatar/forum.hhf.technology/hhf.technoloy/32/68_2.png) [@hhf.technoloy](https://forum.hhf.technology/u/hhf.technoloy)\
**Post date:** [October 19, 2025, 6:18pm UTC](https://forum.hhf.technology/t/setting-up-wireguard-with-ipv6-support-in-docker-on-ubuntu/3894/1 "2025-10-19T18:18:32Z")

</div>

# Setting Up WireGuard with IPv6 Support in Docker on Ubuntu

This guide provides a streamlined method to deploy WireGuard in a Docker container on Ubuntu Server 24.04, enabling unique global IPv6 addresses for clients. It addresses a common gap in existing documentation, particularly for IPv6 delegation. The setup uses host networking for simplicity in managing static IPv6 routes, though a bridge networking alternative is included.

**Key Goals:**

- Assign unique global IPv6 addresses from a delegated prefix to each WireGuard client.
- Support delegation to downstream devices (e.g., via a travel router client).

**Assumptions and Scope:**

- Ubuntu Server 24.04 host.
- At least one /64 IPv6 subnet available from your delegated prefix (e.g., /56 or /48; /60 works but limits subnets).
- Static IPv6 prefix preferred; dynamic prefixes require manual reconfiguration (see [IPv6 Prefix Changes](#ipv6-prefix-changes)).
- Use the [LinuxServer.io](http://LinuxServer.io) WireGuard image, which lacks official IPv6 support but works with manual tweaks.
- Commands are for Ubuntu; adapt for other distributions.

**Security Notes:**

- Keep your IPv6 prefix private.
- Use a DDNS service or static IPv4 for the endpoint if your public IP changes.

## Prerequisites

### IPv6 Setup

1. Obtain a delegated IPv6 prefix from your ISP (e.g., via router WAN settings).
2. Request a /56 or /48 for flexibility; a /60 suffices but yields fewer /64 subnets.
3. Carve out /64 subnets: one for WireGuard clients (e.g., `2001:db8:b00b:42a::/64`), and optionally another for delegation (e.g., `2001:db8:b00b:42b::/64`).

### Enable IPv6 Packet Forwarding

Edit `/etc/sysctl.conf` as root to enable forwarding:

```auto
net.ipv4.ip_forward=1
net.ipv6.conf.all.forwarding=1

```

Apply changes:

```auto
sudo sysctl -p

```

### Install Dependencies

Update packages and install WireGuard tools (with optional `qrencode` for QR codes):

```auto
sudo apt update
sudo apt install wireguard-tools qrencode

```

Install Docker (official repository recommended over Ubuntu’s version):

```auto
# Add GPG key
sudo apt install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg

# Add repository
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

sudo apt update
sudo apt install docker-ce docker-compose-plugin

```

Add your user to the Docker group (log out and back in after):

```auto
sudo usermod -aG docker $USER

```

## Deploy the WireGuard Container

Create a working directory:

```auto
sudo mkdir -p /srv/wireguard/config
cd /srv/wireguard

```

Create `docker-compose.yaml`:

```yaml
services:
  wireguard:
    image: linuxserver/wireguard:latest
    container_name: wireguard
    network_mode: host
    cap_add:
      - NET_ADMIN
      - SYS_MODULE
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=America/Los_Angeles # Adjust to your timezone
      - SERVERURL=your.ddns.example.com # Your public endpoint
      - SERVERPORT=51820
      - PEERS=phone,workphone,tablet,laptop,travelrouter # Comma-separated peer names
      - PEERDNS=8.8.8.8,8.8.4.4,2001:4860:4860::8888,2001:4860:4860::8844
      - INTERNAL_SUBNET=10.13.13.0/24
      - ALLOWEDIPS=0.0.0.0/0,::/0
      - PERSISTENTKEEPALIVE_PEERS=all
    volumes:
      - ./config:/config
      - /lib/modules:/lib/modules
    privileged: true
    restart: unless-stopped

```

Launch the container:

```auto
sudo docker compose up -d
sudo docker compose logs wireguard # Check for errors

```

**Note:** If using Ubuntu’s Docker (`docker-compose` with hyphen), adjust commands accordingly.

## Test IPv4 Connectivity

1. Forward UDP port 51820 on your router to the host’s static IPv4.
2. Allow the port on the host firewall (if using UFW):

```auto
sudo ufw allow 51820/udp

```

3. Generate and scan a client QR code (e.g., from `/srv/wireguard/config/peer_phone/peer_phone.png`) using the WireGuard app.
4. Connect via mobile data (disable WiFi) and verify IPv4 internet access.

## Enable IPv6 Support

Edit `/srv/wireguard/config/wg_confs/wg0.conf` to add IPv6. Include `ip6tables` rules in `PostUp`/`PostDown` for IPv6 forwarding (e.g., `ip6tables -A FORWARD -i %i -j ACCEPT; ip6tables -A FORWARD -o %i -j ACCEPT`).

**Server Interface Example:**

```auto
[Interface]
Address = 10.13.13.1/32, 2001:db8:b00b:42a::1/128
ListenPort = 51820
PrivateKey = <server-private-key>
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth+ -j MASQUERADE; ip6tables -A FORWARD -i %i -j ACCEPT; ip6tables -A FORWARD -o %i -j ACCEPT
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth+ -j MASQUERADE; ip6tables -D FORWARD -i %i -j ACCEPT; ip6tables -D FORWARD -o %i -j ACCEPT

```

**Peer Examples (add IPv6 to `AllowedIPs`):**

```auto
[Peer]
# peer_phone
PublicKey = <public-key>
PresharedKey = <preshared-key>
AllowedIPs = 10.13.13.2/32, 2001:db8:b00b:42a::2/128
PersistentKeepalive = 25

[Peer]
# peer_travelrouter
PublicKey = <public-key>
PresharedKey = <preshared-key>
AllowedIPs = 10.13.13.6/32, 2001:db8:b00b:42a::6/128, 2001:db8:b00b:42b::/64
PersistentKeepalive = 25

```

Edit each client config in `/srv/wireguard/config/peer_*/peer_*.conf` (add IPv6 to `Address`):

**Client Example:**

```auto
[Interface]
Address = 10.13.13.2, 2001:db8:b00b:42a::2
PrivateKey = <client-private-key>
DNS = 8.8.8.8,8.8.4.4,2001:4860:4860::8888,2001:4860:4860::8844

[Peer]
PublicKey = <server-public-key>
PresharedKey = <preshared-key>
Endpoint = your.ddns.example.com:51820
AllowedIPs = 0.0.0.0/0, ::/0

```

Restart and verify:

```auto
sudo docker compose restart wireguard
sudo docker compose logs wireguard

```

Regenerate QR codes (originals lack IPv6):

```auto
qrencode -o peer_phone.png < /srv/wireguard/config/peer_phone/peer_phone.conf
qrencode -t ANSI < /srv/wireguard/config/peer_phone/peer_phone.conf # Terminal display

```

**Warning:** Changes to `docker-compose.yaml` (e.g., peers, DNS) will overwrite configs; back them up post-edits.

## Configure Static IPv6 Routes

1. Identify the host’s link-local IPv6 on the LAN interface (e.g., `eth0`):

2. On your router, add static routes for client subnets (e.g., `2001:db8:b00b:42a::/64`, `2001:db8:b00b:42b::/64`) via the link-local address on the LAN interface.

Test: Reconnect a client and verify IPv6 connectivity (e.g., `ping6 ipv6.google.com`).

## Alternative: Docker Bridge Networking

For bridge mode (avoids host networking but adds route complexity):

Update `docker-compose.yaml` (add `sysctls` for IPv6):

```yaml
networks:
  wg6:
    enable_ipv6: true
    ipam:
      driver: default
      config:
        - subnet: "2001:db8:b00b:421::/64"

services:
  wireguard:
    # ... (remove network_mode: host)
    networks:
      - wg6
    ports:
      - 51820:51820/udp
    sysctls:
      - net.ipv6.conf.all.disable_ipv6=0
      - net.ipv6.conf.all.forwarding=1
      - net.ipv6.conf.default.forwarding=1
      - net.ipv6.conf.eth0.proxy_ndp=1
    # ... (rest unchanged)

```

1. Enable IPv6 in Docker daemon (`/etc/docker/daemon.json`):

2. Restart: `sudo docker compose up -d`.

3. Get container IPv6: `sudo docker exec wireguard ip -c -6 -brief addr show dev eth0` (e.g., `2001:db8:b00b:421::2`).

4. Add host routes (non-persistent):

5. Persist routes via systemd (create `/etc/systemd/system/wg-routes.service`):

## Handling IPv6 Prefix Changes

If your prefix changes (e.g., Xfinity residential), update:

- `/srv/wireguard/config/wg_confs/wg0.conf`
- All `/srv/wireguard/config/peer_*/peer_*.conf`
- `docker-compose.yaml` (bridge mode only)
- Router static routes
- Host/container routes (if bridge mode)

Restart the container and regenerate QR codes.

## Troubleshooting

Common issues stem from Docker’s limited IPv6 handling, routing misconfigurations, or firewall rules. Use these steps to diagnose and resolve.

### Container Startup Failures

- **Privileges/Capabilities Missing** : Ensure `privileged: true`, `cap_add: [NET_ADMIN, SYS_MODULE]`, and `/lib/modules` volume are set. Check logs for kernel module errors: `sudo docker compose logs wireguard | grep module`.
- **Docker Version Conflicts** : Recent versions (e.g., 28.0.0+) may break IPv6 on Ubuntu 24.04. Verify with `docker --version`; rollback if needed: `sudo apt install docker-ce=5:27.5.1-1~ubuntu.24.04~noble` (adjust per repo).
- **Module Loading** : Run `sudo modprobe wireguard` on host; restart container.

### No IPv6 Connectivity in Container

- **IPv6 Disabled** : For bridge mode, confirm `/etc/docker/daemon.json` has `"ipv6": true` and restart Docker. Inside container: `sudo docker exec wireguard ip -6 route`—should show routes like `default via <gateway> dev eth0`.
- **Sysctl Issues** : Add `net.ipv6.conf.all.disable_ipv6=0` to sysctls; verify host forwarding: `sysctl net.ipv6.conf.all.forwarding`.
- **Route Table Check** : Run `sudo docker exec wireguard ip -c -6 route`—look for wg0 entries and defaults. If missing, add ip6tables forwarding rules to PostUp.

### Clients Not Receiving/Using IPv6 Addresses

- **Config Overwrites** : After edits, regenerate QR codes and rescan; verify client `Address` includes IPv6 (e.g., `10.13.13.2, 2001:db8:b00b:42a::2`).
- **AllowedIPs Errors** : If clients fail to connect, temporarily remove `::/0` from client AllowedIPs, test IPv4, then re-add.
- **DNS Resolution** : Ensure PEERDNS includes IPv6 servers; test client: `nslookup ipv6.google.com` over tunnel.

### Routing and Forwarding Problems

- **Static Routes Missing** : Verify router routes via link-local; on host: `ip -6 route show`. For bridge, add host-to-container routes.
- **Packet Tracing** : Use `tcpdump -i any ip6` on host/container/router to trace IPv6 packets—check if requests reach destination and replies return.
- **Policy-Based Routing for Endpoints** : If tunnel traffic loops to endpoints, add to wg0.conf PostUp (symmetric on peers):

```auto
ip -6 rule add to <peer-endpoint-ipv6>/128 ipproto udp dport 51820 table 200
ip -6 route add default via fe80::<router-link-local> dev <lan-iface> table 200

```

PreDown: Delete rules/routes. This bypasses tunnel for UDP handshakes.
- **NDP Proxy** : For bridge mode, add `net.ipv6.conf.eth0.proxy_ndp=1` sysctl; install radvd on host to announce client prefixes.

### Firewall and NAT Conflicts

- **UFW/ip6tables Blocks** : Run `sudo ufw status verbose` and `sudo ip6tables -L -v -n`—allow 51820/udp and ::/0 forwarding. Add `ufw allow in on wg0` if using UFW.
- **ISP/Router Blocks** : Confirm prefix delegation; test host IPv6: `ping6 ipv6.google.com`.

### General Diagnostics

- **WireGuard Status** : `sudo docker exec -it wireguard wg show`—check handshakes, allowed IPs.
- **Full Logs** : `sudo docker compose logs -f wireguard` for real-time errors.
- **Reboot and Test** : After changes, reboot host/router; incrementally test IPv4 → full IPv6.

If issues persist, share `ip -6 route`, wg show output, and logs on comments below will try to help
