Alcançar destino IPv6-only sem mexer no host
O problema
Alguns serviços gerenciados só publicam AAAA (IPv6), sem registro A.
O caso clássico é o Supabase no free tier: db.<ref>.supabase.co resolve
só pra IPv6. Como as redes bridge do Docker nascem IPv4-only, o container
não consegue abrir a conexão — e o deploy fica em restart loop com um erro
genérico de driver de banco (Unable to determine Dialect without JDBC metadata, connection refused, etc.), que não parece um problema de rede.
Por que a solução "óbvia" mexe no host
O caminho intuitivo é criar uma rede dual-stack (docker network create --ipv6 --subnet fd00:.../64 ...) e listá-la no networks: do app. Isso funciona, mas
tem um custo: o container passa a fazer o egress IPv6 ele mesmo, e pra isso
o kernel precisa rotear os pacotes da bridge pra NIC física — o que exige
net.ipv6.conf.all.forwarding=1.
Esse sysctl é um parâmetro global do host, não da app, e ligá-lo tem um
efeito colateral perigoso: o kernel para de aceitar Router Advertisements
(RA). Num host que pega o IPv6 via SLAAC/RA (comum em cloud), isso derruba
o IPv6 do próprio host, a menos que você paree com accept_ra=2 na interface.
Ou seja: uma config de app acaba precisando de uma mutação de kernel que pode
tirar o servidor do ar.
Em Docker 28+ o
docker network create --ipv6já liga o forwarding sozinho ao subir a bridge. O ponto continua valendo: é o host sendo alterado, com o risco de RA acima.
A solução: relay IPv4→IPv6 no host
Em vez do container fazer o egress IPv6, um relay no namespace do host recebe a conexão em IPv4 e a repassa em IPv6. O container fica na bridge normal, 100% IPv4, e o Traefik ingress continua intacto.
app container (bridge normal, IPv4)
│ conecta em 172.17.0.1:5432 (gateway do host — IPv4, SEM forwarding)
▼
relay no host namespace (socat / haproxy)
│ abre conexão pro [db.<ref>.supabase.co]:5432 via IPv6
▼ (é o HOST originando o tráfego)
destino IPv6-onlyPor que não precisa de forwarding: o sysctl governa rotear pacotes de
terceiros entre interfaces. Os dois saltos aqui são isentos disso — o container
falando com seu próprio gateway é comunicação local com a bridge; e o relay,
por estar no host, faz o host originar a conexão IPv6, que usa a stack do
host direto (tráfego local não passa pela lógica de forwarding).
Prova (validada em Docker 29, host OVH com `forwarding=0`)
| Teste | Resultado |
|---|---|
Host originando IPv6 (forwarding=0) |
✅ alcança — tráfego local dispensa forwarding |
| Container v6 direto (sem rede ipv6) | ❌ falha — sem rota v6 |
| Container → relay → endpoint v6-only | ✅ o destino enxerga o IPv6 público do host |
O net.ipv6.conf.all.forwarding permaneceu 0 o tempo todo, e o container
tinha zero endereços IPv6.
Como configurar
1. O relay (systemd, resiliente)
# /etc/systemd/system/relay-supabase.service
[Unit]
Description=Relay IPv4->IPv6 para Supabase (db)
After=network-online.target docker.service
[Service]
# socat: escuta na bridge do host em IPv4, repassa pro Supabase em IPv6
ExecStart=/usr/bin/socat TCP4-LISTEN:5432,fork,reuseaddr,bind=172.17.0.1 TCP6:db.<ref>.supabase.co:5432
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.targetsystemctl daemon-reload
systemctl enable --now relay-supabaseAlternativa em container (sem instalar socat no host):
# serviço de relay, no host namespace
services:
relay-supabase:
image: alpine/socat
network_mode: host
restart: always
command: TCP4-LISTEN:5432,fork,reuseaddr,bind=172.17.0.1 TCP6:db.<ref>.supabase.co:54322. O app aponta pro relay (não pro hostname Supabase)
# .deploy.yml
environment:
DB_HOST: "172.17.0.1" # gateway/host, IPv4 puro
DB_PORT: "5432"O app fica na bridge normal, com Traefik ingress e isolamento preservados. Nada
de --ipv6, nada de forwarding, nada de rede custom.
Ressalvas
- TLS/Postgres: o Postgres negocia TLS dentro da conexão (não por SNI no handshake TCP), então o relay TCP cru funciona e o certificado do Supabase valida pelo hostname normalmente.
- HTTPS com SNI: pra alvos HTTPS o relay tem que ser TCP puro (passthrough) e o cliente precisa mandar o SNI/Host certo — não é o caso de banco, mas atente se for relay pra uma API HTTPS.
- Um relay por destino: limpo pra 1–2 endpoints (banco, storage). Pra egress IPv6 arbitrário não escala — aí a conversa é outra (ver abaixo).
- Resiliência: use
systemdcomRestart=always(ou haproxy com health check).socatsolto num terminal morre fácil.
Quando o relay não é o ideal
- Some com o problema na fonte: o Supabase oferece IPv4 via o pooler (Supavisor) e via add-on de IPv4 dedicado. Se disponível, trocar a connection string elimina a necessidade de egress IPv6 — sem relay, sem sysctl.
- Egress IPv6 amplo (não um endpoint só): aí cabe
macvlan/ipvlancom um prefixo/64roteado (cada container ganha um IPv6 público próprio, sem forwarding) — mas exige espaço de endereço real do provedor e sai da bridgepublic, quebrando o Traefik-por-nome. Bom pra worker, não pra app com ingress.