O Dev Container não alcança um host só-VPN: onde olhar
O navegador e o contêiner têm contextos diferentes de resolução, proxy e rotas. Descubra qual o processo que falha usa antes de alterar qualquer configuração de VPN.
A API privada abre no navegador do desktop e falha no contêiner de desenvolvimento. Isso não é contradição e raramente é a própria VPN. A requisição do navegador e a requisição que falha foram feitas por processos diferentes, com resolvedores, proxies e rotas distintos, e muitas vezes a partir de espaços de nomes de rede completamente diferentes. «No meu laptop» pode significar o host Windows, uma distribuição WSL, a máquina virtual do Docker Desktop e um contêiner dentro dessa VM — quatro contextos que cada um pode responder de forma diferente ao mesmo nome de host. Descubra qual o processo que falha usa antes de mexer em uma única configuração.
Localize o processo antes de mudar uma configuração
Comece pelo motor de contêineres, não pelo cliente VPN. Pergunte com qual contexto a linha de comando fala e em qual sistema operacional esse motor roda, porque um contêiner Linux gerenciado por uma VM de desktop segue um caminho diferente de um gerenciado por um daemon local:
docker context ls
docker info --format '{{.OperatingSystem}} / {{.Name}}'
docker run --rm alpine ip route
No Windows, wsl -l -v diz se a distribuição roda sob WSL 2, o que a coloca atrás do próprio comutador virtual. Esse detalhe decide se o contêiner compartilha a tabela de rotas do host ou fica atrás de um NAT que precisa de uma porta encaminhada ou de um nome de host para chegar ao host.
Mantenha uma requisição constante e compare
Escolha um endpoint inofensivo no host privado e use-o em toda comparação: do navegador, de um terminal do host, da distribuição WSL e de dentro do contêiner. Anote quatro coisas por tentativa: o endereço resolvido, se um proxy foi usado, a classe de falha e o tempo decorrido. Resolução de nome, estabelecimento de conexão, validação TLS e um status HTTP são quatro problemas diferentes, e misturá-los em «não funciona» desperdiça uma manhã.
Execute a comparação dentro do contêiner que falha, com um tempo limite para que um firewall silencioso não segure o terminal por minutos. Um contêiner novo reproduz o contexto de rede do motor, mas não o espaço de nomes, as variáveis de proxy nem o resolvedor do contêiner da aplicação, então docker exec no contêiner real é o teste autoritativo, e um contêiner auxiliar é apenas uma forma de isolar se a diferença está no motor ou no contêiner:
docker exec -it app /bin/sh -c "getent hosts api.internal; curl -v --max-time 8 https://api.internal/ 2>&1 | tail -20"
docker run --rm --network container:app curlimages/curl:latest -sv --max-time 8 https://api.internal/ 2>&1 | tail -20
A imagem base alpine traz busybox e não traz curl, e esse é exatamente o tipo de detalhe que faz um bloco de diagnóstico copiado falhar: escolha uma imagem com o cliente ou adicione-o na mesma execução. Uma comparação com sites públicos sem relação não isola nada. Se https://example.com funciona a partir do contêiner, você aprendeu que o contêiner tem acesso à internet e nada sobre se ele alcança um endereço privado pelo túnel. Da mesma forma, abrir um terminal novo só prova que o terminal novo funciona, não que compartilhe um runtime com a aplicação que falhou.
O Docker Desktop pode funcionar com uma VPN
A própria documentação do Docker sobre VPN e conectividade com o host descreve isso como uma configuração suportada, e o resumo honesto é que funciona em vez de estar automaticamente quebrado. Não presuma que a VM de desktop contorne o túnel do host por design; em muitas configurações o tráfego segue corretamente o túnel do host, e é por isso que a lenda de que «o Docker ignora a VPN» produz conselhos confiantes e errados.
O que ainda importa é o backend em uso, a política de split tunnel do cliente e a configuração de resolvedor que a VM recebeu. Um cliente VPN que instala rotas mas não DNS, ou que empurra um servidor DNS que a VM não alcança, produz uma falha de resolução de nome dentro do contêiner enquanto o host funciona porque tem um resolvedor extra ou uma resposta em cache. É um problema de resolvedor disfarçado de contêiner. Sobrevive a cada reconexão VPN que você tentar, porque o túnel já transporta o tráfego que você pediu.
Sobreposições de endereço e MTU são falhas diferentes
Duas falhas merecem diagnóstico próprio porque costumam ser confundidas com política de acesso. A primeira é uma sobreposição de endereços: se a VPN empurra um intervalo que colide com a sub-rede da ponte padrão de uma rede de contêineres, a rota do contêiner para esse intervalo aponta para a ponte local e nunca chega ao túnel. Inspecione as sub-redes reais em vez de adivinhar:
docker network inspect bridge --format '{{range .IPAM.Config}}{{.Subnet}}{{end}}'
ip route get 10.20.30.40
A segunda é um problema de MTU. Se a conexão se estabelece e o TLS conclui mas as respostas maiores travam, você tem um problema de tamanho de pacote, não de autorização, e substituir registros DNS nunca vai corrigir isso. Teste com uma requisição pequena e uma grande antes de mudar qualquer coisa, e anote os dois resultados.
A correção para uma sobreposição é outra sub-rede de contêiner ou uma rota restrita, escolhida por quem é dono do plano de endereços. A correção para um travamento de MTU é fixar ou reduzir a MTU do túnel ou da interface para um valor que sobreviva ao caminho, aplicado na camada que você controla.
A configuração de proxy é por processo, não por máquina
Dentro de um contêiner, localhost nomeia o contexto de rede do próprio contêiner, então um proxy rodando no host não é acessível ali com esse nome. O Docker Desktop fornece host.docker.internal para acesso ao host, e no Linux você o adiciona explicitamente com --add-host=host.docker.internal:host-gateway. De qualquer forma, esse nome dá uma rota até o host; não cria um serviço de proxy nem autoriza você a usar um.
Use o endereço e a porta de proxy fornecidos para o ambiente real, e verifique quem os lê. Variáveis exportadas em um shell interativo não configuram automaticamente cada extensão de editor, servidor de linguagem, ferramenta de build e daemon em segundo plano. O que importa é o processo que fez a requisição que falha, então inspecione o ambiente dele e não o do seu terminal.
Por fim, faça a correção se explicar sozinha. Uma solução útil nomeia a fronteira que estava errada, a configuração que a mudou e o conjunto de processos que ela agora afeta. Reiniciar a VPN, o editor e o motor em sequência pode mudar o sintoma, mas não deixa nada que a próxima pessoa possa usar.