Conteúdo

Tutorial de redes Docker: bridge, redes personalizadas e comunicação entre contêineres

Quando um aplicativo precisa se conectar a um banco de dados, cache ou outro contêiner, você precisa entender as redes Docker. A ideia mais importante é: coloque os contêineres que precisam se comunicar na mesma rede personalizada para que eles possam se conectar usando os nomes uns dos outros.

Diagrama de uma rede personalizada Docker e do mapeamento de portas
O navegador acessa o app pela porta do host mapeada com -p; na rede personalizada, o app se conecta ao banco de dados pelo nome db, sem que o banco precise ser exposto externamente

Drivers de rede

Uma instalação nova do Docker tem três redes integradas:

$ docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
a74caef4acb9   bridge    bridge    local
b9a49b39804b   host      host      local
87a8a7fd2cd9   none      null      local
DriverDescrição
bridgePadrão. O contêiner tem seu próprio IP em uma sub-rede virtual
hostO contêiner usa diretamente a rede do host, sem isolamento (funciona melhor no Linux)
noneSem rede
overlayEntre vários hosts (Docker Swarm)
macvlanFaz o contêiner aparecer na rede local como uma máquina física

Limitações da bridge padrão

Se você não especificar uma rede, o contêiner se conectará à rede padrão chamada bridge. Nessa rede, os contêineres não podem se conectar pelo nome:

$ docker run -d --name web1 nginx:1.30
b1d3fb9024c112c5990d9ac85012cd3f5e38c114ab4409a06f960c6d8cdb496e

$ docker run --rm alpine:3.23 ping -c 1 -W 2 web1
ping: bad address 'web1'

$ docker inspect --format '{{.NetworkSettings.IPAddress}}' web1

template parsing error: template: :1:18: executing "" at <.NetworkSettings.IPAddress>: map has no entry for key "IPAddress"

$ docker inspect --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' web1
172.18.0.2

$ docker run --rm alpine:3.23 ping -c 1 172.18.0.2
PING 172.18.0.2 (172.18.0.2): 56 data bytes
64 bytes from 172.18.0.2: seq=0 ttl=64 time=0.058 ms

--- 172.18.0.2 ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 0.058/0.058/0.058 ms

$ docker rm -f web1
web1
  • Não é possível encontrar o nome web1 (bad address), mas é possível se conectar usando o IP 172.18.0.2.
  • Porém, o Docker atribui o IP automaticamente, e ele pode mudar quando o contêiner for recriado. Por isso, não o fixe na configuração.

Mudança no Docker 29: nas versões anteriores, você podia usar {{.NetworkSettings.IPAddress}} para obter o IP do contêiner. O Docker 29 removeu esse campo (veja o template parsing error acima). Use {{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}.

Por isso, na prática, quase sempre se cria uma rede personalizada.

Rede personalizada: comunicação pelo nome

docker network create appnet
docker run -d --name db --network appnet -e POSTGRES_PASSWORD=secret postgres:18
$ docker network create appnet
fadfeb6028cb78335fa94cfd7536265256a914cc461caae1026b6ebb21e9d7a0

$ docker run -d --name db --network appnet -e POSTGRES_PASSWORD=secret postgres:18
227033e27591e6a1c195a6d63a141aaf4486c6285477792fe3080e1d4deeb2c0

$ docker run --rm --network appnet alpine:3.23 ping -c 2 db
PING db (172.19.0.2): 56 data bytes
64 bytes from 172.19.0.2: seq=0 ttl=64 time=0.069 ms
64 bytes from 172.19.0.2: seq=1 ttl=64 time=0.092 ms

--- db ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.069/0.080/0.092 ms

$ docker run --rm --network appnet -e PGPASSWORD=secret postgres:18 psql -h db -U postgres -c "SELECT version();"
                                                      version
--------------------------------------------------------------------------------------------------------------------
 PostgreSQL 18.6 (Debian 18.6-1.pgdg13+2) on x86_64-pc-linux-gnu, compiled by gcc (Debian 14.2.0-19) 14.2.0, 64-bit
(1 row)


$ docker network inspect appnet --format "{{range .Containers}}{{.Name}} {{.IPv4Address}}{{println}}{{end}}"
db 172.19.0.2/16
  • Na rede appnet, o DNS integrado do Docker resolve o nome db para o IP do contêiner do banco de dados.
  • O cliente PostgreSQL se conectou usando -h db e consultou a versão do banco de dados (PostgreSQL 18.6).
  • O contêiner do banco de dados não usa -p, então o host e sistemas externos não conseguem acessá-lo. Somente os contêineres na mesma rede podem, o que é mais seguro.

A configuração de conexão do aplicativo também usa o nome do contêiner, por exemplo:

docker run -d --name app --network appnet -p 3000:3000 \
  -e DATABASE_URL=postgres://postgres:secret@db:5432/postgres \
  myapp

Atenção:

  • A conexão entre contêineres usa a porta interna do contêiner (5432), independentemente de -p.
  • Só os serviços que precisam ser acessados pelo host ou externamente (como um site) precisam de -p.
  • Evite usar nomes de contêiner como web ou app, que também são palavras usadas como domínios de nível superior, como destino da conexão. Dentro da mesma rede, não há problema, mas consultas feitas fora dela podem resolver para endereços inesperados e confundir a depuração.

Um contêiner em várias redes e aliases de rede

$ docker network create appnet
77fadbc5633854c9e75fd9aa65325d0155ad33f5de453abac7f15f6766402bee

$ docker run -d --name app --network appnet nginx:1.30
25b4c1d92caeef5ca8765daf2f3ad6247982cacfad4914000ded8a0a455df851

$ docker network create frontend
45a9ab78fddb9f1b47a22b9ce156144286888c42eadc2b4f589986fe1f98cee0

$ docker network connect frontend app

$ docker inspect --format '{{range $k, $v := .NetworkSettings.Networks}}{{$k}} {{$v.IPAddress}}{{println}}{{end}}' app
appnet 172.19.0.2
frontend 172.20.0.2


$ docker run -d --name search --network appnet --network-alias es nginx:1.30
43b07713e7ec032d21e37fdf690064b389f798ed26b2715e7ea18c15840647af

$ docker run --rm --network appnet alpine:3.23 getent hosts es
172.19.0.3        es  es

$ docker rm -f app search
app
search

$ docker network rm appnet frontend
appnet
frontend
  • docker network connect adiciona um contêiner em execução a outra rede. Agora, app tem dois IPs. Isso é usado com frequência para isolamento: a rede frontend conecta o Nginx ao aplicativo, e a rede backend conecta o aplicativo ao banco de dados. Assim, o Nginx não consegue acessar o banco de dados.
  • --network-alias es permite que outros contêineres na mesma rede também usem o nome es para se conectar a search. Quando vários contêineres usam o mesmo alias, o DNS retorna vários IPs.

Gerenciar redes

docker network create appnet
docker network ls
docker network inspect appnet               # Veja os contêineres e IPs
docker network connect appnet nome-do-contêiner       # Adicione o contêiner em execução à rede
docker network disconnect appnet nome-do-contêiner
docker network rm appnet
docker network prune                        # Remova redes não utilizadas

Mapeamento de portas: o aplicativo precisa escutar em 0.0.0.0

-p 8000:8000 faz com que o Docker encaminhe o tráfego enviado à porta 8000 do host para a porta 8000 na interface de rede do contêiner. Por isso, o aplicativo dentro do contêiner precisa escutar em 0.0.0.0 (todas as interfaces), e não apenas em 127.0.0.1 (o localhost do próprio contêiner). Compare na prática com o servidor web integrado do Python:

$ docker run -d --name localonly -p 8000:8000 python:3.14-slim python -m http.server 8000 --bind 127.0.0.1
246edb96a9affefaab188ac7d5ceec2fb103b511bdac352cca6ed3a80ae6764a

$ curl -sS http://localhost:8000
curl: (52) Empty reply from server

$ docker rm -f localonly
localonly

$ docker run -d --name allif -p 8000:8000 python:3.14-slim python -m http.server 8000 --bind 0.0.0.0
438fa44edf6aec790a7e6b2bfcdb7af33c16a14c661c50b8d9564ff52844e5e6

$ curl -s http://localhost:8000 | head -n 3
<!DOCTYPE HTML>
<html lang="en">
<head>

$ docker rm -f allif
allif
  • Quando o aplicativo escuta apenas em 127.0.0.1, a porta está mapeada, mas o curl retorna Empty reply from server: o tráfego chegou ao contêiner, mas o aplicativo está aguardando apenas no localhost do próprio contêiner e não recebe a conexão.
  • Ao configurar a escuta em 0.0.0.0, tudo funciona.

Muitos servidores de desenvolvimento escutam em localhost por padrão. Ao executá-los em um contêiner, especifique o endereço:

flask run --host 0.0.0.0
uvicorn main:app --host 0.0.0.0
vite --host 0.0.0.0

Conectar-se ao host a partir de um contêiner

Para permitir que um contêiner acesse um serviço no host (por exemplo, um banco de dados em execução no host), use o nome especial host.docker.internal:

$ docker run --rm alpine:3.23 getent hosts host.docker.internal

$ docker run --rm --add-host=host.docker.internal:host-gateway alpine:3.23 getent hosts host.docker.internal
172.18.0.1        host.docker.internal  host.docker.internal
  • Docker Desktop (Windows, macOS): oferece suporte integrado a host.docker.internal.
  • Linux: esse nome não existe por padrão (o primeiro comando não retorna nenhuma saída). Adicione --add-host=host.docker.internal:host-gateway; o nome será resolvido para o endereço do host na rede Docker.

O serviço no host também precisa escutar em uma interface acessível pelo contêiner, e não apenas em 127.0.0.1.

Rede host e rede none

docker run -d --network host nginx:1.30

O contêiner usa diretamente a interface de rede do host. Se o nginx escutar na porta 80, essa será a porta 80 do host, sem precisar de -p (se você informar essa opção, ela será ignorada). O desempenho é o melhor possível, mas não há isolamento e pode haver conflito de portas com aplicativos no host. O comportamento no Docker Desktop não é exatamente igual ao do Linux.

Com --network none, o contêiner fica totalmente sem rede e tem apenas a interface de loopback local:

$ docker run --rm --network none alpine:3.23 ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever

É útil para tarefas que não precisam de rede ou não devem se conectar a ela (por exemplo, processar arquivos não confiáveis).

Solução de problemas

Não é possível conectar contêineres usando localhost

Cada contêiner tem seu próprio localhost. Se você se conectar a localhost:5432 dentro do contêiner app, estará se conectando ao próprio app, e não ao contêiner do banco de dados. Use o nome do contêiner, por exemplo, db:5432.

Não é possível conectar ao contêiner a partir do host

Verifique nesta ordem:

  1. Confira com docker ps se o campo PORTS contém 0.0.0.0:8080->80/tcp.
  2. Verifique se o aplicativo dentro do contêiner está escutando em 0.0.0.0 (Empty reply from server, mostrado acima, é um sintoma típico).
  3. Verifique se o firewall do host ou o grupo de segurança na nuvem permite essa porta.

O contêiner não consegue acessar a internet

Geralmente, é um problema de DNS. Você pode especificar o DNS em /etc/docker/daemon.json (no Docker Desktop, em Settings → Docker Engine). Reinicie o Docker depois da alteração:

{ "dns": ["1.1.1.1", "8.8.8.8"] }

Ao usar Docker Compose, o Compose cria automaticamente uma rede para o projeto. Os serviços podem se conectar diretamente usando os nomes uns dos outros.

Resumo

  • A rede bridge padrão não oferece resolução de nomes. Use uma rede personalizada para conectar contêineres.
  • Em uma rede personalizada, conecte os contêineres usando seus nomes ou --network-alias.
  • O aplicativo precisa escutar em 0.0.0.0 para que o mapeamento de portas funcione.
  • Para um contêiner se conectar ao host, use host.docker.internal.

Exercícios

Exercício 1: Crie a rede lab, inicie nela um contêiner redis:8-alpine chamado cache e, em seguida, use outro contêiner para testar com redis-cli -h cache ping.

Resposta
docker network create lab
docker run -d --name cache --network lab redis:8-alpine
docker run --rm --network lab redis:8-alpine redis-cli -h cache ping

A saída é PONG. Consulte os comandos de rede em docker network.

Perguntas frequentes

Por que localhost dentro de um contêiner não conecta a outro contêiner?

Cada contêiner tem sua própria rede, e localhost aponta para o próprio contêiner. Para se conectar a outro contêiner, coloque os dois na mesma rede personalizada e use o nome do contêiner.

Como um contêiner se conecta a um serviço em execução no host?

No Docker Desktop, use host.docker.internal; no Linux, adicione --add-host host.docker.internal:host-gateway. Veja “Como conectar um contêiner ao host” neste capítulo.

Como obter o IP de um contêiner no Docker 29?

{{.NetworkSettings.IPAddress}} foi removido. Use docker inspect -f "{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}" contêiner.

Atualizado 14 de outubro de 2026