Skip to content

Instantly share code, notes, and snippets.

@davidar
Last active April 11, 2026 00:54
Show Gist options
  • Select an option

  • Save davidar/ff6d1460596b93c5967075a3e41dac4b to your computer and use it in GitHub Desktop.

Select an option

Save davidar/ff6d1460596b93c5967075a3e41dac4b to your computer and use it in GitHub Desktop.
Running Docker on an IPv6-only Hetzner VPS — full setup guide

Running Docker on an IPv6-Only VPS (Hetzner)

Hetzner's cheapest VPS plans come with IPv6-only connectivity. Docker doesn't handle this well out of the box — containers won't be able to reach the internet. Here's the full setup.

The Problem

Docker defaults to IPv4 networking. On an IPv6-only host, containers can't resolve DNS or reach external services (package registries, APIs, OAuth providers, etc.). You'll see:

  • apt-get update failing inside containers
  • API calls timing out
  • OAuth flows breaking
  • Anything that needs external connectivity silently failing

Step 1: Enable IPv6 in Docker Daemon

/etc/docker/daemon.json:

{
  "ipv6": true,
  "fixed-cidr-v6": "2001:db8:1::/64"
}

Then restart Docker:

sudo systemctl restart docker

This enables IPv6 on the default bridge network.

Step 2: Enable IPv6 in Every Docker Compose Project

The daemon setting only covers the default bridge. Each Docker Compose project creates its own network, and each one needs IPv6 explicitly enabled:

# In your docker-compose.yml
networks:
  default:
    enable_ipv6: true
    ipam:
      config:
        - subnet: fd00::/80  # Use a unique subnet per project

Use unique subnets to avoid conflicts between projects:

  • Project A: fd00::/80
  • Project B: fd01::/80
  • Project C: fd02::/80

Step 3: Port Binding

On an IPv6-only host, you probably want to bind to 127.0.0.1 and put a reverse proxy or tunnel in front:

services:
  myapp:
    ports:
      - "127.0.0.1:8080:8080"  # Only accessible locally

Then expose via Cloudflare Tunnel, nginx, or similar. Binding to 0.0.0.0 would expose the port but it's only reachable over IPv6 anyway (unless you have Tailscale or similar for IPv4).

Common Gotchas

Forgetting IPv6 on one compose project

Symptoms: the app starts fine but can't reach external APIs. Easy to miss because the container itself works — it just can't talk to the outside world.

Subnet conflicts

If two compose projects use the same fd00::/80 subnet and both are running, the second one will fail to create its network. Keep a simple registry of which subnet each project uses.

DNS resolution

Docker's embedded DNS should work over IPv6 once the daemon is configured. If you hit DNS issues, check that your host's /etc/resolv.conf has working IPv6 nameservers.

Verifying

# Check Docker network IPv6 status
docker network ls --format "table {{.Name}}\t{{.Driver}}\t{{.IPv6}}"

# Test connectivity from inside a container
docker exec <container> curl -6 ifconfig.me

# Check daemon config
cat /etc/docker/daemon.json

Getting IPv4 Anyway

For services that only support IPv4 endpoints, you have a few options:

  • Tailscale with exit node: Route IPv4 traffic through a Tailscale exit node (e.g. Mullvad). Beware of the Docker localhost routing issue this creates.
  • NAT64/DNS64: Some providers offer this at the network level.
  • Cloudflare Tunnel: Handles IPv4 on Cloudflare's end, your server only needs IPv6 outbound to Cloudflare.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment