SSD Nodes Learn 8GB de RAM — $66/ano
Guias Matt ConnorPor Matt Connor · Atualizado 2026-08-02

Redes no Docker Compose: DNS, host e portas publicadas

Entenda a rede bridge padrão, DNS pelo nome do serviço, quando usar network_mode: host, como compartilhar redes e por que uma porta publicada ignora o UFW.

O que o Compose cria antes de iniciar seu aplicativo

A rede do Docker Compose começa com uma regra: docker compose up cria uma rede privada para o projeto, conecta todos os serviços a ela e permite que esses serviços se comuniquem usando o nome do serviço. Você não precisa escrever uma única linha networks: para obter isso. Grande parte da confusão sobre redes no Compose ocorre porque o padrão já está disponível.

Este é um arquivo pequeno. Salve-o como compose.yaml em um diretório chamado shop.

services:
  web:
    image: nginx:1.27
    ports:
      - "8080:80"
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: example

Inicie o projeto e veja o que o Docker criou:

docker compose up -d
docker network ls

A lista agora contém uma rede chamada shop_default. O Compose a nomeia como <project>_default, e o nome do projeto assume como padrão o nome do diretório em minúsculas. Substitua esse nome usando docker compose -p myproject up -d ou definindo name: myproject no nível superior do arquivo. O driver é bridge, que funciona como um switch virtual dentro do host. Cada container recebe um endereço em uma sub-rede privada, e o tráfego de saída é traduzido para o endereço do host ao sair.

docker compose down exclui essa rede novamente. Por isso, um container antigo de outro projeto pode manter uma rede aberta: o Docker recusa a operação com error while removing network: network shop_default has active endpoints, e a correção é parar ou remover o container que ainda está conectado a ela.

Se você ainda não conhece o Compose, vale ler primeiro Estrutura do arquivo Compose e comandos do ciclo de vida, porque todo o conteúdo a seguir pressupõe que você sabe iniciar e parar um projeto.

DNS pelo nome do serviço é a parte que iniciantes não percebem

Em qualquer rede definida pelo usuário, o Docker executa um servidor DNS incorporado que cada container acessa em 127.0.0.11. Ele resolve os nomes dos serviços para os endereços atuais dos containers. Assim, web acessa o banco de dados pelo hostname db, na porta 5432, sem nenhuma configuração.

docker compose exec web getent hosts db

Isso exibe uma linha como 172.18.0.2 db. Se não exibir nada, os dois serviços não estão na mesma rede.

O erro que quase todo mundo comete uma vez é usar localhost na configuração da aplicação. Dentro de um container, localhost é esse próprio container, não o host nem o outro serviço. Os clientes do Postgres informam isso claramente:

could not connect to server: Connection refused
	Is the server running on host "localhost" (127.0.0.1) and accepting
	TCP connections on port 5432?

A string de conexão deve ser postgresql://postgres:example@db:5432/postgres. A parte do host é o nome do serviço.

Dois detalhes economizam tempo mais tarde. Os nomes são resolvidos para o que estiver em execução no momento. Portanto, docker compose up -d --scale web=3 fornece um nome com três endereços, e um cliente que mantém o DNS armazenado em cache indefinidamente ficará associado a um container inativo. Além disso, a rede legada bridge, usada por um docker run simples sem --network, não oferece resolução de nomes. Por isso, as orientações sobre links entre containers de 2016 não correspondem ao que você vê.

Você não precisa de ports: para conectar dois serviços

ports: publica uma porta do contêiner no host. Ele é usado para tráfego que chega de fora do Docker. Isso não tem relação com o tráfego entre serviços, que já funciona em todo o intervalo de portas na rede do projeto.

Por isso, o ports: - "5432:5432" que muitas pessoas adicionam ao serviço de banco de dados não ajuda e causa um problema real: ele expõe o Postgres na interface pública do servidor. Remova-o. Se quiser acessá-lo do seu laptop para uma migração, faça o bind ao loopback com "127.0.0.1:5432:5432" e acesse-o por um túnel SSH. A diferença entre um socket em escuta, uma porta publicada e uma regra de firewall é explicada em como funcionam as portas e os serviços em escuta no Linux.

expose: serve apenas como documentação no Compose. Ele não abre nada, porque nada foi fechado entre os contêineres na mesma rede.

Quando network_mode host é adequado e quais são os custos

O modo host remove o namespace de rede próprio do container e permite que o processo use diretamente as interfaces do host.

services:
  probe:
    image: alpine:3.20
    network_mode: host
    command: sleep infinity

Há motivos concretos para usar esse modo. Um processo que precisa receber tráfego de broadcast ou multicast na rede local, como a descoberta de dispositivos para um servidor de mídia ou um hub de automação residencial, não consegue vê-lo atrás de uma bridge, porque a bridge não encaminha esse tráfego para o container. Um agente de monitoramento que lê os contadores das interfaces do host precisa usar as interfaces do host. Além disso, você elimina o salto de tradução de endereços, o que é relevante com altas taxas de pacotes.

Os custos são específicos.

ports: deixa de funcionar. O Docker avisa que as portas publicadas são descartadas ao usar o modo de rede host, e o container faz bind na porta usada pelo processo. Dois containers no modo host que tentem usar a porta 8080 entram em conflito, e o segundo falha com bind: address already in use.

A resolução de nomes por nome de serviço deixa de funcionar nos dois sentidos. O container não está na rede do projeto, portanto não consegue resolver db, e os outros serviços também não conseguem resolvê-lo. Ele só consegue acessá-los pelas portas publicadas no host, geralmente em 127.0.0.1.

O isolamento deixa de existir. Um processo que faça bind em 0.0.0.0 dentro de um container no modo host fica escutando em todas as interfaces do servidor, incluindo a interface pública, exatamente como um pacote instalado com apt. Há uma vantagem: esse tráfego segue o caminho normal de entrada, portanto as regras do UFW se aplicam a ele, o que não ocorre com portas publicadas.

O modo host é um recurso do Linux Docker Engine. O Docker Desktop oferece suporte a ele somente a partir da versão 4.34 e apenas depois que você o habilita. Além disso, os containers não podem fazer bind nos endereços IP do host, e somente TCP e UDP são tratados. Se metade da equipe usa servidores Linux e a outra metade usa Docker Desktop, espere que o mesmo arquivo se comporte de forma diferente.

Use o modo host quando precisar das interfaces do host. Não o use para corrigir um problema de conexão, porque ele normalmente troca um problema por outro mais difícil.

Conectar dois projetos Compose com uma rede externa

Uma rede criada por um projeto não fica visível para outro. Por isso, um proxy reverso em proxy/compose.yaml não consegue acessar um app em app/compose.yaml, mesmo no mesmo servidor. A solução é usar uma rede que não pertença a nenhum dos projetos.

Crie-a uma vez, manualmente:

docker network create edge

Depois, declare-a como externa em cada projeto. No lado do proxy:

services:
  proxy:
    image: traefik:v3.1
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    networks:
      - edge

networks:
  edge:
    name: edge
    external: true

No lado da aplicação:

services:
  app:
    image: nginx:1.27
    networks:
      - edge
      - internal
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: example
    networks:
      - internal

networks:
  edge:
    name: edge
    external: true
  internal:

external: true informa ao Compose para se conectar a uma rede existente em vez de criar uma nova e para mantê-la no lugar em docker compose down. A chave name: separada é mais importante do que parece: sem ela, o Compose procura uma rede com o nome exatamente igual a edge. Com ela, você pode usar um nome para a rede no arquivo e outro no host.

Se a rede não existir, o Compose se recusa a iniciar e informa que a rede foi declarada como externa, mas não foi encontrada. Crie-a primeiro.

Observe o que o arquivo da aplicação faz com internal. O banco de dados fica apenas nessa rede local do projeto. Portanto, o proxy não consegue acessá-lo, e somente app consegue. Adicionar internal: true a uma rede vai além e remove completamente a rota dela para o mundo externo. Esse é um bom padrão para um banco de dados, mas há um custo que você deve conhecer antes de aplicá-lo: um container em uma rede interna não consegue baixar nada. Portanto, um entrypoint que execute apt-get update ou pip install na inicialização ficará aguardando e depois falhará por timeout.

Para ver uma configuração completa, com regras de roteamento e certificados, consulte executar vários apps atrás de uma única instância do Traefik.

Portas publicadas ignoram o UFW

Esta é a parte da rede do Compose que se transforma em um incidente de segurança. Você publica uma porta, verifica que o UFW está ativo e nega tudo exceto SSH, mas o serviço continua acessível pela internet.

sudo ufw status
curl http://203.0.113.10:8080

O UFW informa que a porta está bloqueada. O curl retorna a página mesmo assim. Nada está quebrado. O Docker grava suas próprias regras de tradução de endereços e encaminhamento diretamente no iptables. O tráfego destinado a uma porta publicada do contêiner é encaminhado para o contêiner, em vez de ser entregue ao host. Por isso, ele nunca passa pela cadeia que o UFW gerencia para o tráfego destinado localmente. As regras do Docker também são avaliadas antes das regras do UFW.

A correção rápida é publicar somente onde necessário:

    ports:
      - "127.0.0.1:8080:80"

Isso vincula o lado do host à interface de loopback. Assim, a porta fica acessível pelo próprio servidor e por um túnel SSH, mas não por nenhum outro lugar. Coloque o ponto de entrada público atrás de um proxy reverso que publique 80 e 443 intencionalmente. A explicação completa, incluindo a cadeia DOCKER-USER para os casos em que você precisa filtrar uma porta publicada, está em por que o Docker publica diretamente além do UFW e como corrigir isso.

Como depurar em quatro comandos

Comece verificando em qual rede cada container realmente está:

docker network inspect shop_default

O bloco Containers lista todos os containers conectados, com seus endereços. Um serviço ausente nessa lista está em outra rede, está no modo host ou não está em execução.

Teste a resolução de nomes a partir de um container temporário conectado à mesma rede. Assim, você não precisa de ferramentas dentro das suas próprias imagens:

docker run --rm --network shop_default busybox nslookup db
docker run --rm --network shop_default busybox nc -zv db 5432

A falha de nslookup aponta para um problema de resolução de nomes ou de associação à rede. Se nslookup funcionar, mas nc falhar, o serviço está em execução, mas não está escutando nessa porta ou está escutando em 127.0.0.1 dentro do próprio container, em vez de 0.0.0.0. Isso é comum em servidores de desenvolvimento. A correção está no endereço de bind da aplicação, não no Docker.

Há outra falha que parece um problema do Docker. Se os containers conseguirem se comunicar entre si, mas não conseguirem acessar uma máquina na rede do escritório ou da VPN, a sub-rede do Docker provavelmente está sobreposta a essa rede. Por padrão, o Docker aloca redes a partir de 172.17.0.0/16. Altere o pool em /etc/docker/daemon.json:

{
  "default-address-pools": [
    { "base": "10.200.0.0/16", "size": 24 }
  ]
}

Depois, execute sudo systemctl restart docker e recrie as redes afetadas, porque uma rede existente mantém a sub-rede usada quando foi criada.

FAQ

Por que meus containers não conseguem alcançar uns aos outros pelo nome do serviço?

Eles não estão na mesma rede. O Compose coloca cada serviço automaticamente em <project>_default, mas, assim que você adiciona uma lista networks: a um serviço, essa lista se torna o conjunto completo de redes dele, e a rede padrão deixa de ser implícita. Execute docker network inspect <network> e verifique se os dois containers aparecem no bloco Containers. Verifique também se nenhum serviço usa network_mode: host, porque um container no modo host não está em nenhuma rede Docker e não consegue resolver nomes de serviços.

Preciso publicar portas para um serviço alcançar outro?

Não. Em uma rede do Compose, todas as portas de todos os containers ficam acessíveis aos outros containers nessa rede. ports: existe apenas para expor um container ao tráfego externo ao Docker, e expose: serve como documentação. Publicar a porta de um banco de dados é uma prática comum e arriscada, pois coloca o banco na interface pública do servidor.

Qual é a diferença entre a rede bridge e a rede host?

A bridge fornece ao container seu próprio namespace de rede e endereço em um switch virtual, com resolução automática de nomes entre containers e tradução do tráfego de saída. A host fornece diretamente ao container a pilha de rede do host: não há endereço separado, resolução por nome de serviço, publicação de portas nem isolamento em relação aos outros processos que escutam no host. Bridge é o padrão e a escolha adequada, exceto quando o processo precisa das interfaces do host.

Como conecto containers de dois arquivos Compose diferentes?

Crie uma rede compartilhada com docker network create edge. Em seguida, declare-a nos dois arquivos com external: true e conecte os serviços que precisam se comunicar. O Compose não criará nem excluirá essa rede. Se você ignorar a etapa de criação, o Compose se recusará a iniciar e informará que a rede foi declarada como externa, mas não foi encontrada.

Por que meu container pode ser acessado pela internet quando o UFW bloqueia a porta?

Porque uma porta publicada é processada pelas regras de encaminhamento que o Docker adiciona ao iptables. Essas regras são avaliadas antes das regras do UFW, e o tráfego encaminhado também não passa pela cadeia que o UFW filtra. Vincule o lado do host ao loopback com "127.0.0.1:8080:80" e coloque tudo que for público atrás de um proxy reverso nas portas 80 e 443.