SSD Nodes Learn Hosting plans →
Guias Matt ConnorPor Matt Connor · Atualizado 2026-10-12

Redes no Docker Compose: DNS, host e portas

Entenda a rede bridge padrão, o DNS pelo nome do serviço, o modo host, redes entre projetos e a porta publicada que pode ignorar as regras do UFW.

O que o Compose cria antes de iniciar a sua aplicação

A rede do Docker Compose começa com uma regra: docker compose up cria uma rede privada para o projeto, liga todos os serviços a ela e permite que esses serviços comuniquem entre si pelo nome do serviço. Não é necessário escrever uma única linha networks: para obter esse comportamento. Grande parte da confusão sobre as redes do Compose resulta de não saber que esse comportamento já existe por predefinição.

Eis um ficheiro pequeno. Guarde-o como compose.yaml num 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 passa a incluir uma rede chamada shop_default. O Compose atribui-lhe o nome <project>_default, e o nome do projeto corresponde, por predefinição, ao nome do diretório em minúsculas. Substitua esse nome com docker compose -p myproject up -d ou com um name: myproject de nível superior no ficheiro. O driver é bridge, que corresponde a um switch virtual dentro do host. Cada contentor recebe um endereço numa sub-rede privada, e o tráfego de saída é traduzido para o endereço do host no percurso para o exterior.

docker compose down elimina novamente essa rede. É por isso que um contentor antigo de um projeto anterior pode manter uma rede aberta: o Docker recusa a operação com error while removing network: network shop_default has active endpoints, e a solução é parar ou remover o contentor que ainda está ligado a ela.

Se o Compose é novidade para si, vale a pena ler primeiro a estrutura do ficheiro Compose e os comandos do ciclo de vida, porque tudo o que se segue pressupõe que consegue iniciar e parar um projeto.

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

Em qualquer rede definida pelo utilizador, o Docker executa um servidor DNS incorporado que cada contentor vê em 127.0.0.11. Esse servidor resolve os nomes dos serviços para os endereços atuais dos contentores. Assim, web chega à base de dados através do nome de host db, na porta 5432, sem qualquer configuração.

docker compose exec web getent hosts db

Isto imprime uma linha semelhante a 172.18.0.2 db. Se não imprimir nada, os dois serviços não estão na mesma rede.

O erro que quase toda a gente comete uma vez é usar localhost na configuração da aplicação. Dentro de um contentor, localhost é o próprio contentor, não o host nem o outro serviço. Os clientes de Postgres mostram 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 ligação deve ser postgresql://postgres:example@db:5432/postgres. A parte do host é o nome do serviço.

Há dois detalhes que evitam problemas mais tarde. Os nomes resolvem para o que está em execução naquele momento. Por isso, docker compose up -d --scale web=3 devolve um nome com três endereços, e um cliente que mantenha o DNS em cache indefinidamente ficará preso a um contentor inativo. Além disso, a rede legada bridge, usada por um docker run simples sem --network, não consegue resolver nomes de contentores: os contentores nessa rede só se encontram através do endereço IP e consultam nomes de host externos através de uma cópia do ficheiro /etc/resolv.conf do host. É por isso que as recomendações sobre links de contentores de 2016 não correspondem ao que observa atualmente.

Não é necessário usar ports: para ligar dois serviços

ports: publica uma porta do contentor no host. Serve para tráfego que chega de fora do Docker. Não tem qualquer relação com o tráfego entre serviços, que já funciona em todo o intervalo de portas da rede do projeto.

Por isso, o ports: - "5432:5432" que muitas pessoas adicionam ao serviço da base de dados não traz qualquer benefício e causa um problema real: expõe o Postgres na interface pública do servidor. Remova-o. Se precisar de aceder ao serviço a partir do portátil para fazer uma migração, associe-o à interface de loopback com "127.0.0.1:5432:5432" e aceda através de 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. Não abre nada, porque não havia nada fechado entre contentores na mesma rede.

Quando usar network_mode host e quais são os custos

O modo host elimina o namespace de rede próprio do contentor e permite que o processo utilize diretamente as interfaces do host.

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

Há motivos concretos para usar este modo. Um processo que precise de ver tráfego broadcast ou multicast na rede local, como a deteção de dispositivos de um servidor multimédia ou de um hub de automação doméstica, não consegue vê-lo atrás de uma bridge, porque a bridge não encaminha esse tráfego para o contentor. Um agente de monitorização que leia os contadores das interfaces do host precisa das interfaces do host. Também elimina o salto de tradução de endereços, o que pode ser importante com taxas elevadas 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 contentor associa-se às portas que o processo realmente utilizar. Dois contentores em modo host que tentem usar a porta 8080 entram em conflito, e o segundo termina com bind: address already in use.

A resolução por nome de serviço desaparece nos dois sentidos. O contentor não está na rede do projeto, por isso não consegue resolver db, e os outros serviços também não o conseguem resolver. Ainda pode chegar a esses serviços através das portas publicadas no host, normalmente em 127.0.0.1. No Linux, também pode chegar diretamente a um contentor numa bridge através do endereço IP desse contentor, em qualquer porta onde esteja a escutar, porque o host tem acesso aberto aos contentores nas suas redes bridge e um contentor em modo host partilha a pilha de rede do host. Esses endereços mudam quando os contentores são recriados, por isso uma porta publicada em loopback é a rota estável.

O isolamento desaparece. Um processo que faça bind de 0.0.0.0 dentro de um contentor em modo host fica a escutar em todas as interfaces do servidor, incluindo a interface pública, exatamente como um pacote instalado com apt. Há uma vantagem: este tráfego segue o percurso normal de entrada, pelo que as regras do UFW aplicam-se a ele. Isso não acontece com as portas publicadas.

O modo host é uma funcionalidade do Docker Engine para Linux. O Docker Desktop só o suporta a partir da versão 4.34 e apenas depois de o ativar. Existem ainda as limitações de que os contentores não podem fazer bind de endereços IP do host e apenas são tratados TCP e UDP. Se metade da equipa usar servidores Linux e a outra metade usar Docker Desktop, espere que o mesmo ficheiro tenha comportamentos diferentes.

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

Ligar dois projetos Compose com uma rede externa

Uma rede criada por um projeto não fica visível para outro. Por isso, um reverse proxy em proxy/compose.yaml não consegue ver uma aplicação 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 ligar a uma rede existente em vez de criar uma, e para a deixar no local em docker compose down. A chave name: separada é mais importante do que parece: sem ela, o Compose procura uma rede chamada exatamente edge. Com ela, pode atribuir um nome à rede no ficheiro e outro nome no host.

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

Observe o que o ficheiro da aplicação faz com internal. A base de dados fica apenas nessa rede local do projeto, por isso o proxy não consegue aceder-lhe e apenas app consegue. Adicionar internal: true numa rede vai mais longe e remove completamente a rota dessa rede para o exterior. Esta é uma boa predefinição para uma base de dados, mas há um custo que deve conhecer antes de a definir: um contentor numa rede interna não consegue descarregar nada. Por isso, um entrypoint que execute apt-get update ou pip install no arranque ficará bloqueado e depois falhará com um timeout.

Para ver uma configuração completa, com regras de encaminhamento e certificados, consulte executar várias aplicações atrás de uma instância do Traefik.

Portas publicadas contornam o UFW

Esta é a parte da rede do Compose que pode transformar-se num incidente de segurança. Publica uma porta, confirma que o UFW está ativo e bloqueia tudo exceto o SSH, mas o serviço continua acessível a partir da Internet.

sudo ufw status
curl http://203.0.113.10:8080

O UFW indica que a porta está bloqueada. O curl devolve a página mesmo assim. Nada está avariado. O Docker escreve as suas próprias regras de tradução de endereços e encaminhamento diretamente no iptables. O tráfego destinado a uma porta publicada do contentor é encaminhado para o contentor, em vez de ser entregue ao host. Por isso, nunca passa pela cadeia que o UFW gere para o tráfego destinado localmente. As regras do Docker também são avaliadas antes das regras do UFW.

A correção curta é publicar apenas onde for necessário:

    ports:
      - "127.0.0.1:8080:80"

Isto associa o lado do host à interface de loopback. A porta fica acessível a partir do próprio servidor e através de um túnel SSH, mas não a partir de outros locais. Coloque o ponto de entrada público atrás de um reverse proxy que publique deliberadamente as portas 80 e 443. A explicação completa, incluindo a cadeia DOCKER-USER para os casos em que é necessário filtrar uma porta publicada, está em por que o Docker publica diretamente além do UFW e como corrigir isso.

Como depurar isto com quatro comandos

Comece por verificar em que rede cada contentor está realmente:

docker network inspect shop_default

O bloco Containers lista todos os contentores associados, com os respetivos endereços. Um serviço ausente dessa lista está noutra rede, em modo host ou não está em execução.

Teste a resolução de nomes a partir de um contentor temporário associado à mesma rede. Assim, não precisa de instalar ferramentas nas suas próprias imagens:

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

Uma falha em nslookup aponta para um problema de resolução de nomes ou de pertença à rede. Se nslookup tiver sucesso enquanto nc falha, o nome e a rede estão corretos e o problema está na própria ligação. Se nc comunicar Connection refused, a causa habitual é o serviço não estar a escutar nessa porta ou estar a escutar em 127.0.0.1 dentro do próprio contentor, em vez de 0.0.0.0. Isto é comum em servidores de desenvolvimento. A correção está no endereço de bind da aplicação, não no Docker. Se nc ficar bloqueado e terminar por timeout, procure algo que esteja a descartar o tráfego pelo caminho, como uma regra de firewall, ou um contentor pausado ou que não esteja a responder.

Há ainda outro problema que parece um erro do Docker. Se os contentores comunicarem entre si, mas não conseguirem chegar a uma máquina da rede do escritório ou da VPN, é provável que a sub-rede do Docker se sobreponha a essa rede. Por predefiniçã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 com que foi criada.

FAQ

Por que os meus containers não conseguem comunicar entre si pelo nome do serviço?

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

Preciso publicar portas para um serviço comunicar com outro?

Não. Numa rede Compose, todas as portas de todos os containers estão acessíveis aos outros containers dessa rede. ports: existe apenas para expor um container ao tráfego proveniente do exterior do Docker, e expose: serve apenas como documentação. Publicar a porta de uma base de dados é um hábito comum e dispendioso, porque coloca a base de dados na interface pública do servidor.

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

A bridge dá ao container o seu próprio namespace de rede e um endereço num switch virtual, com resolução automática de nomes entre containers e tradução do tráfego de saída. A host dá ao container acesso direto à pilha de rede do host: não há um endereço separado, resolução por nome de serviço, publicação de portas nem isolamento em relação aos outros listeners do host. A bridge é a opção predefinida e a correta, exceto quando o processo precisa das interfaces do host.

Como ligo containers de dois ficheiros Compose diferentes?

Crie uma rede partilhada com docker network create edge. Depois, declare-a nos dois ficheiros com external: true e ligue os serviços que precisam de comunicar. O Compose não a cria nem a elimina. Se ignorar a etapa de criação, o Compose recusa iniciar e indica que a rede foi declarada como externa, mas não foi encontrada.

Por que o meu container está acessível 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 chain que o UFW filtra. Ligue o lado do host ao loopback com "127.0.0.1:8080:80" e coloque tudo o que for público atrás de um reverse proxy nas portas 80 e 443.

Este artigo foi útil?