Alternar tema

Como acessar o host a partir de um contêiner Docker: guia completo do host.docker.internal

Easton editorial illustration: observability control panel

Era sexta-feira, três da tarde, e eu estava olhando para a mensagem de erro no terminal: Connection refused.

Para ser sincero, naquele momento eu já estava bem frustrado. O MySQL local funcionava normalmente, o Navicat conseguia se conectar e o terminal também. Mas a aplicação dentro do contêiner simplesmente não conectava. Revisei a string de conexão três vezes: localhost:3306. Parecia certa. O usuário e a senha também estavam corretos. Então, onde estava o problema?

No fim, descobri que o culpado era justamente localhost.

Se você já passou por algo parecido — tentar acessar um serviço do host com localhost ou 127.0.0.1 dentro de um contêiner Docker e receber erro — este artigo é para você. Vou explicar da forma mais direta possível por que o localhost do contêiner não é o localhost que você imagina e como usar o domínio especial host.docker.internal para resolver o problema de forma elegante.

Neste artigo, você vai aprender:

  • Como funciona, de verdade, o isolamento de rede dos contêineres, sem excesso de jargão
  • A configuração correta no macOS, Windows e Linux
  • Um checklist prático de diagnóstico para consultar quando o problema aparecer novamente

Por que localhost não funciona?

Resposta curta: o contêiner tem seu próprio ambiente de rede.

Parece abstrato? Pense no contêiner como uma casa independente, com seu próprio endereço, sua própria caixa de correio e tudo mais. Quando você chama localhost ou digita 127.0.0.1 dentro do contêiner, está procurando essa própria casa, não a máquina que está do lado de fora.

Na prática:

  • No host, localhost aponta para o próprio host
  • No contêiner, localhost aponta para o próprio contêiner
  • São dois localhost completamente diferentes

Também fiquei surpreso quando entendi isso pela primeira vez. O MySQL estava funcionando perfeitamente no meu computador; por que o contêiner não conseguia encontrá-lo? Porque ele procurava o MySQL dentro do próprio ambiente, onde o serviço não existia.

Como funciona o isolamento de rede do contêiner

O Docker cria um “namespace de rede” separado para cada contêiner. Não se assuste com o termo. Na prática, significa que cada contêiner tem sua própria interface de rede, seu próprio endereço IP e sua própria tabela de roteamento. É como sua casa e a casa do vizinho: mesmo no mesmo prédio, cada uma tem sua própria senha de Wi-Fi.

O contêiner e o host são conectados por uma ponte virtual chamada docker0. Em geral, o IP do contêiner se parece com 172.17.0.x, e o IP do host visto pelo contêiner costuma ser 172.17.0.1, o endereço do gateway da ponte.

Quando você acessa localhost dentro do contêiner, está acessando o 127.0.0.1 do contêiner, não o 127.0.0.1 do host. Por isso a conexão com o MySQL do host falha.

Veja mensagens de erro típicas:

Error: connect ECONNREFUSED 127.0.0.1:3306

Ou:

Can't connect to MySQL server on 'localhost' (111)

Esses erros são clássicos quando se tenta acessar um serviço do host usando localhost dentro do contêiner.

O que é host.docker.internal?

Se localhost não funciona, como o contêiner pode acessar o host?

O Docker oferece uma solução bastante prática: host.docker.internal. Esse domínio especial é resolvido para o endereço IP do host. Você pode entendê-lo como um “apelido” do host: não importa qual seja o IP real, esse nome leva até ele.

Por exemplo, se o MySQL estiver escutando na porta 3306 do host, use esta conexão dentro do contêiner:

mysql://user:[email protected]:3306/dbname

Você não precisa saber se o IP do host é 192.168.1.100 ou 10.0.0.5, nem se preocupar quando ele mudar após uma troca de rede. host.docker.internal apontará para o endereço correto.

Prático, não é?

Versões e plataformas compatíveis

Aqui existe uma diferença importante entre plataformas.

Usuários de macOS e Windows com Docker Desktop

Se você usa o Docker Desktop, a versão com interface gráfica, host.docker.internal tem suporte nativo desde a versão 18.03, lançada em março de 2018. Funciona imediatamente, sem configuração adicional.

Basta usar host.docker.internal no código:

const mysql = require('mysql2');
const connection = mysql.createConnection({
  host: 'host.docker.internal',  // Simples assim
  port: 3306,
  user: 'root',
  password: 'your_password'
});

Usuários de Linux com Docker Engine

No Linux, a situação exige um passo extra. O Docker é executado diretamente no sistema, sem a máquina virtual intermediária usada no macOS e no Windows, por isso host.docker.internal não existe por padrão.

A boa notícia é que, desde o Docker Engine 20.10, lançado em dezembro de 2020, você pode ativá-lo manualmente. A próxima seção mostra como fazer isso.

Se sua versão do Docker for mais antiga, ainda há algumas alternativas:

  • Usar 172.17.0.1, o IP do gateway da ponte padrão do Docker
  • Usar o IP real do host na rede Docker
  • Usar docker.for.mac.host.internal, somente em versões antigas do Docker para macOS

Como configurar nas três principais plataformas

Esta é a parte prática, com configurações prontas para copiar.

Configuração no macOS e Windows com Docker Desktop

Este é o cenário mais simples.

Método 1: usar diretamente no código

Não é necessária nenhuma configuração adicional. Basta usar host.docker.internal:

# docker-compose.yml
version: '3'
services:
  app:
    image: myapp:latest
    environment:
      - DB_HOST=host.docker.internal  # Uso direto
      - DB_PORT=3306

Método 2: declarar explicitamente, se quiser

Embora não seja obrigatório, você também pode adicionar extra_hosts para tornar a configuração explícita:

version: '3'
services:
  app:
    image: myapp:latest
    extra_hosts:
      - "host.docker.internal:host-gateway"
    environment:
      - DB_HOST=host.docker.internal

host-gateway é uma sintaxe disponível no Docker 20.10+ e representa o endereço do gateway do host.

Se você inicia o contêiner com docker run:

docker run -d \
  --add-host=host.docker.internal:host-gateway \
  -e DB_HOST=host.docker.internal \
  myapp:latest

Configuração no Linux com Docker Engine

No Linux, é necessário configurar o mapeamento manualmente.

Método 1: opção recomendada com host-gateway

Este é o método mais genérico e funciona em todas as plataformas com Docker 20.10+:

# docker-compose.yml
version: '3'
services:
  app:
    image: myapp:latest
    extra_hosts:
      - "host.docker.internal:host-gateway"  # Configuração essencial
    environment:
      - DB_HOST=host.docker.internal
      - DB_PORT=3306

Com docker run:

docker run -d \
  --add-host=host.docker.internal:host-gateway \
  -e DB_HOST=host.docker.internal \
  myapp:latest

A principal vantagem é a compatibilidade entre plataformas: a mesma configuração funciona no macOS, Windows e Linux, sem ajustes específicos para cada sistema.

Método 2: usar o IP da ponte Docker como alternativa

Se host-gateway não estiver disponível por causa de uma versão antiga do Docker, use o endereço do gateway da ponte padrão:

version: '3'
services:
  app:
    image: myapp:latest
    extra_hosts:
      - "host.docker.internal:172.17.0.1"  # Gateway padrão do Docker
    environment:
      - DB_HOST=host.docker.internal

172.17.0.1 é o gateway padrão da rede bridge do Docker. Na maioria dos casos esse IP está correto, a menos que você tenha alterado a configuração padrão de rede.

Método 3: última alternativa com o modo de rede host

Se nenhum dos métodos anteriores funcionar, existe uma opção mais radical:

docker run -d \
  --network=host \
  -e DB_HOST=localhost \  # Agora localhost pode ser usado
  myapp:latest

Ou no Docker Compose:

version: '3'
services:
  app:
    image: myapp:latest
    network_mode: "host"  # Usa a rede do host
    environment:
      - DB_HOST=localhost  # Agora localhost funciona diretamente

Vantagem desse método: é simples e direto. O contêiner usa a pilha de rede do host, então localhost é realmente o localhost do host.

Desvantagens desse método:

  • Remove o isolamento de rede do contêiner
  • O contêiner e o host compartilham portas, o que pode causar conflitos, por exemplo se ambos precisarem da porta 8080
  • Funciona apenas no Linux; macOS e Windows não oferecem o mesmo suporte
  • Não é recomendado em produção; use apenas para desenvolvimento e depuração local

Configuração multiplataforma recomendada

Se parte da equipe usa macOS e outra parte usa Linux, ou se o código precisa funcionar em ambientes diferentes, adote esta configuração:

# docker-compose.yml
version: '3'
services:
  app:
    image: myapp:latest
    extra_hosts:
      - "host.docker.internal:host-gateway"  # Funciona em todas as plataformas
    environment:
      - DB_HOST=host.docker.internal
      - DB_PORT=3306
      - DB_USER=root
      - DB_PASSWORD=your_password

Essa configuração funciona em todas as plataformas com Docker 20.10+, lançado no fim de 2020. Se você ainda usa uma versão anterior a 2020, sinceramente, já está na hora de atualizar.

Pontos importantes na configuração dos serviços do host

Configurar o contêiner é apenas metade do trabalho.

O serviço no host também precisa estar configurado corretamente. Caso contrário, a conexão continuará falhando. Esse detalhe costuma passar despercebido, por isso merece uma seção própria.

O serviço precisa escutar no endereço correto

Este é o problema mais comum.

Muitos serviços escutam apenas em 127.0.0.1 por padrão, aceitando conexões somente da própria máquina. Mas o contêiner Docker não é considerado parte desse localhost: a solicitação chega pela ponte de rede do Docker e acaba recusada.

Você precisa configurar o serviço para escutar em 0.0.0.0, ou seja, aceitar conexões em todas as interfaces de rede.

Configuração do MySQL

Localize o arquivo de configuração do MySQL. Normalmente ele fica em:

  • Linux: /etc/mysql/mysql.conf.d/mysqld.cnf
  • macOS com Homebrew: /usr/local/etc/my.cnf
  • Windows: C:\ProgramData\MySQL\MySQL Server 8.0\my.ini

Altere bind-address:

[mysqld]
# Antes, talvez estivesse assim
# bind-address = 127.0.0.1

# Altere para
bind-address = 0.0.0.0

Reinicie o MySQL:

# Linux
sudo systemctl restart mysql

# macOS
brew services restart mysql

# Windows
# Reinicie o serviço MySQL no Gerenciador de Serviços

Configuração do Redis

Edite redis.conf, normalmente localizado em /etc/redis/redis.conf ou /usr/local/etc/redis.conf:

# Localize esta linha
bind 127.0.0.1 -::1

# Altere para
bind 0.0.0.0

Reinicie o Redis:

# Linux
sudo systemctl restart redis

# macOS
brew services restart redis

Configuração do PostgreSQL

Edite postgresql.conf:

listen_addresses = '*'  # Escuta em todos os endereços

Você também precisa editar pg_hba.conf para permitir o acesso da rede Docker:

# Adicione esta linha para permitir acesso da rede 172.17.0.0/16
host    all             all             172.17.0.0/16           md5

Configure as permissões do usuário no MySQL

Mesmo que o MySQL escute em 0.0.0.0, ainda existe a camada de permissões.

O MySQL administra permissões com base na combinação “usuário@host de origem”. Por exemplo, root@localhost e root@% são usuários diferentes.

Se o usuário do MySQL só puder acessar a partir de localhost, o contêiner continuará sem conexão. Conceda permissão para o usuário acessar a partir da rede Docker:

-- Opção 1: permitir acesso de qualquer host, simples, mas menos seguro
GRANT ALL PRIVILEGES ON *.* TO 'your_user'@'%' IDENTIFIED BY 'your_password';

-- Opção 2: permitir apenas a rede Docker, mais seguro
GRANT ALL PRIVILEGES ON *.* TO 'your_user'@'172.17.0.%' IDENTIFIED BY 'your_password';

-- Atualizar as permissões
FLUSH PRIVILEGES;

No MySQL 8.0+, a sintaxe é um pouco diferente:

-- Primeiro, crie o usuário
CREATE USER 'your_user'@'%' IDENTIFIED BY 'your_password';

-- Depois, conceda as permissões
GRANT ALL PRIVILEGES ON *.* TO 'your_user'@'%';

FLUSH PRIVILEGES;

Configure o firewall

Em alguns sistemas, o firewall pode bloquear o acesso do contêiner Docker aos serviços do host.

Verifique o estado do firewall:

# Linux (ufw)
sudo ufw status

# Linux (firewalld)
sudo firewall-cmd --state

Permita o acesso da rede Docker, usando a porta 3306 do MySQL como exemplo:

# ufw
sudo ufw allow from 172.17.0.0/16 to any port 3306

# firewalld
sudo firewall-cmd --permanent --zone=public --add-rich-rule='rule family="ipv4" source address="172.17.0.0/16" port port="3306" protocol="tcp" accept'
sudo firewall-cmd --reload

Recomendações de segurança

Escutar em 0.0.0.0 tem riscos: o serviço fica exposto a outras máquinas da rede.

Em produção:

  1. Escute apenas em uma interface específica: se você souber qual interface o Docker usa, limite o serviço a ela.

    bind-address = 172.17.0.1
  2. Combine com regras de firewall: permita somente a rede Docker e bloqueie as demais origens.

  3. Use um contêiner dedicado para o banco de dados: em vez de executar o banco no host, inicie-o com o Docker Compose. Assim, a aplicação e o banco ficam na mesma rede, com mais isolamento.

No ambiente local de desenvolvimento:

Na prática, escutar em 0.0.0.0 costuma ser aceitável no desenvolvimento local. Seu computador não é um servidor e normalmente não está acessível pela internet. Não é preciso entrar em pânico, mas continue atento à rede em que você está conectado.

Checklist de diagnóstico para problemas comuns

A conexão falhou? Não se preocupe. Siga este checklist passo a passo.

Problema 1: Connection refused, ou conexão recusada

Este é o erro mais comum. A mensagem se parece com:

Error: connect ECONNREFUSED host.docker.internal:3306

Ou:

Can't connect to MySQL server on 'host.docker.internal' (111)

Possíveis causas e diagnóstico:

Etapa 1: confirme que o serviço está em execução no host

Execute no host:

# Verificar o MySQL
sudo systemctl status mysql    # Linux
brew services list              # macOS

# Verificar se a porta está sendo escutada
netstat -an | grep 3306
# Ou
lsof -i :3306

Se o serviço estiver parado, inicie-o primeiro.

Etapa 2: verifique o endereço de escuta do serviço

No host:

# Ver em qual endereço o MySQL está escutando
sudo netstat -tlnp | grep 3306

A saída deve se parecer com:

tcp  0  0 0.0.0.0:3306  0.0.0.0:*  LISTEN  1234/mysqld

Observe a terceira coluna. Se ela mostrar 0.0.0.0:3306, o serviço está escutando em todos os endereços. Se mostrar 127.0.0.1:3306, você encontrou o problema: o serviço aceita apenas conexões locais e o contêiner não consegue acessá-lo.

Para corrigir, siga a seção anterior e altere bind-address para 0.0.0.0.

Etapa 3: verifique o firewall

Desative o firewall temporariamente para testar:

# Linux (ufw)
sudo ufw disable

# Linux (firewalld)
sudo systemctl stop firewalld

# macOS
# Ajustes do Sistema -> Privacidade e Segurança -> Firewall -> Desativar

Se a conexão funcionar com o firewall desativado, o problema está nas regras. Configure as permissões mostradas anteriormente e reative o firewall.

Problema 2: Connection timeout, ou tempo limite de conexão

Mensagem de erro:

Error: connect ETIMEDOUT host.docker.internal:3306

Um timeout costuma ser mais difícil de diagnosticar do que uma recusa: o pacote foi enviado, mas não recebeu resposta.

Possíveis causas e diagnóstico:

Etapa 1: verifique se host.docker.internal pode ser resolvido

Execute dentro do contêiner:

# Entrar no contêiner
docker exec -it your_container sh

# Testar com ping
ping host.docker.internal

Se o ping falhar ou retornar “unknown host”, host.docker.internal não foi configurado corretamente.

Atenção, usuários de Linux: confirme que o docker-compose.yml ou o comando docker run contém --add-host=host.docker.internal:host-gateway.

Etapa 2: confirme o número da porta

Tem certeza de que a porta é 3306? O MySQL pode ter sido configurado para outra porta.

Confirme no host:

# Verificar a porta real do MySQL
sudo netstat -tlnp | grep mysqld

Etapa 3: teste a conectividade entre o contêiner e o host

Execute no contêiner:

# Testar se a porta está acessível
telnet host.docker.internal 3306

# Se o contêiner não tiver telnet, use nc
nc -zv host.docker.internal 3306

Se a porta não estiver acessível, verifique novamente o firewall e a configuração do serviço.

Problema 3: Unknown host, ou falha ao resolver host.docker.internal

Mensagem de erro:

getaddrinfo ENOTFOUND host.docker.internal

Esse erro indica falha de resolução DNS: o contêiner não reconhece o domínio host.docker.internal.

Solução:

Adicione extra_hosts à configuração do contêiner:

services:
  app:
    extra_hosts:
      - "host.docker.internal:host-gateway"

Ou adicione a opção ao executar docker run:

docker run --add-host=host.docker.internal:host-gateway ...

Problema 4: falha de autenticação, ou Access denied

Mensagem de erro:

Access denied for user 'root'@'172.17.0.2' (using password: YES)

Isso significa que o contêiner alcançou o MySQL, mas as permissões do usuário estão incorretas.

Solução:

Conceda acesso ao usuário do MySQL:

-- Verificar as permissões atuais do usuário
SELECT user, host FROM mysql.user WHERE user='root';

-- Se existir apenas root@localhost, crie root@% ou [email protected].%
CREATE USER 'root'@'%' IDENTIFIED BY 'your_password';
GRANT ALL PRIVILEGES ON *.* TO 'root'@'%';
FLUSH PRIVILEGES;

Problema 5: configuração diferente entre plataformas

A equipe usa o mesmo docker-compose.yml, mas ele funciona no macOS e falha no Linux.

Solução:

Padronize a opção host-gateway, que funciona em todas as plataformas:

services:
  app:
    extra_hosts:
      - "host.docker.internal:host-gateway"

Confirme que todos usam Docker 20.10 ou posterior. Se alguém ainda usa uma versão antiga, peça para atualizar.

Sequência rápida de diagnóstico

Quando houver um problema de conexão, verifique nesta ordem:

  1. O serviço está em execução?systemctl status / brew services list
  2. Está escutando no endereço correto? → use netstat -tlnp e veja se aparece 0.0.0.0 ou 127.0.0.1
  3. O contêiner está configurado? → verifique extra_hosts ou --add-host
  4. O DNS funciona? → execute ping host.docker.internal no contêiner
  5. A porta está acessível? → teste com telnet ou nc no contêiner
  6. O firewall está bloqueando? → desative-o temporariamente para testar
  7. O usuário tem permissão? → no MySQL, verifique se ele é @localhost ou @%

Na maioria dos casos, o problema estará em uma das três primeiras etapas.

Exemplos práticos

Agora que a teoria está clara, veja alguns casos reais.

Caso 1: aplicação Spring Boot conectando ao MySQL do host

Cenário: você tem um projeto Spring Boot, quer executá-lo com Docker e conectar ao banco de dados MySQL local.

Etapa 1: configure o Spring Boot

Em application.yml:

spring:
  datasource:
    # Use host.docker.internal para conectar ao MySQL do host
    url: jdbc:mysql://host.docker.internal:3306/mydb?useSSL=false&serverTimezone=UTC
    username: root
    password: your_password
    driver-class-name: com.mysql.cj.jdbc.Driver

Etapa 2: configure o Docker Compose

Em docker-compose.yml:

version: '3.8'

services:
  app:
    build: .
    ports:
      - "8080:8080"
    extra_hosts:
      - "host.docker.internal:host-gateway"  # Configuração essencial
    environment:
      # Também é possível substituir os valores por variáveis de ambiente
      SPRING_DATASOURCE_URL: jdbc:mysql://host.docker.internal:3306/mydb
      SPRING_DATASOURCE_USERNAME: root
      SPRING_DATASOURCE_PASSWORD: your_password

Etapa 3: configure o MySQL no host

Edite /etc/mysql/mysql.conf.d/mysqld.cnf:

[mysqld]
bind-address = 0.0.0.0

Reinicie o MySQL:

sudo systemctl restart mysql

Conceda permissão ao usuário:

CREATE USER 'root'@'%' IDENTIFIED BY 'your_password';
GRANT ALL PRIVILEGES ON *.* TO 'root'@'%';
FLUSH PRIVILEGES;

Etapa 4: inicie e teste

docker-compose up --build

Se aparecer uma mensagem como HikariPool-1 - Start completed, a conexão com o banco de dados foi bem-sucedida.

Registro do diagnóstico:

Na primeira vez que fiz essa configuração, recebi Connection refused. O processo foi este:

  1. Verifiquei se o MySQL estava em execução com systemctl status mysql → estava
  2. Verifiquei o endereço de escuta com netstat -tlnp | grep 3306 → encontrei 127.0.0.1:3306
  3. Alterei bind-address para 0.0.0.0 no arquivo de configuração e reiniciei o MySQL
  4. Executei novamente e a conexão funcionou

Caso 2: aplicação Node.js conectando ao Redis do host

Cenário: um projeto Node.js usa Redis como cache e, no desenvolvimento local, o Redis é executado no host.

Etapa 1: código Node.js

// redis-client.js
const redis = require('redis');

const client = redis.createClient({
  host: process.env.REDIS_HOST || 'host.docker.internal',
  port: process.env.REDIS_PORT || 6379,
  // Se o Redis tiver senha configurada
  password: process.env.REDIS_PASSWORD
});

client.on('connect', () => {
  console.log('Redis connected successfully');
});

client.on('error', (err) => {
  console.error('Redis error:', err);
});

module.exports = client;

Etapa 2: configuração do Docker Compose

Em docker-compose.yml:

version: '3.8'

services:
  app:
    build: .
    ports:
      - "3000:3000"
    extra_hosts:
      - "host.docker.internal:host-gateway"
    environment:
      NODE_ENV: development
      REDIS_HOST: host.docker.internal
      REDIS_PORT: 6379

Etapa 3: configure o Redis no host

Edite /etc/redis/redis.conf ou /usr/local/etc/redis.conf:

# Localize a linha bind
bind 127.0.0.1 ::1

# Altere para
bind 0.0.0.0

Se o Redis tiver protected-mode yes, altere também:

protected-mode no  # Aceitável no desenvolvimento local; não faça isso em produção

Reinicie o Redis:

# Linux
sudo systemctl restart redis

# macOS
brew services restart redis

Etapa 4: valide

Inicie a aplicação:

docker-compose up

Ao ver Redis connected successfully, a conexão está funcionando.

Tratamento multiplataforma:

Se a equipe usa macOS e Linux, padronize com uma variável de ambiente:

const REDIS_HOST = process.env.REDIS_HOST || (
  process.platform === 'linux' ? 'host.docker.internal' : 'host.docker.internal'
);

Pensando bem, hoje todas as plataformas podem usar host.docker.internal, sem diferenciação. Basta adicionar extra_hosts: ["host.docker.internal:host-gateway"] ao Docker Compose para que macOS e Linux compartilhem a mesma configuração.

Caso 3: configuração completa do ambiente de desenvolvimento

Este é um modelo prático em que o contêiner da aplicação se conecta ao MySQL e ao Redis do host:

# docker-compose.yml
version: '3.8'

services:
  app:
    build: .
    ports:
      - "8080:8080"
    extra_hosts:
      - "host.docker.internal:host-gateway"
    environment:
      # Configuração do banco de dados
      DB_HOST: host.docker.internal
      DB_PORT: 3306
      DB_NAME: myapp
      DB_USER: root
      DB_PASSWORD: your_password

      # Configuração do Redis
      REDIS_HOST: host.docker.internal
      REDIS_PORT: 6379

      # Configuração da aplicação
      NODE_ENV: development
      PORT: 8080
    volumes:
      - .:/app
      - /app/node_modules  # Não montar node_modules a partir do host
    command: npm run dev  # Hot reload no modo de desenvolvimento

Checklist correspondente para o host:

# MySQL
# Edite /etc/mysql/mysql.conf.d/mysqld.cnf
bind-address = 0.0.0.0
# Reinicie: sudo systemctl restart mysql

# Redis
# Edite /etc/redis/redis.conf
bind 0.0.0.0
protected-mode no
# Reinicie: sudo systemctl restart redis

# Firewall, se necessário
sudo ufw allow from 172.17.0.0/16 to any port 3306
sudo ufw allow from 172.17.0.0/16 to any port 6379

Essa configuração funciona no macOS e no Linux e pode ser reutilizada como ponto de partida.

Resumo

Depois de tudo isso, os três pontos essenciais são:

1. Entenda o princípio

O contêiner tem seu próprio ambiente de rede. Dentro dele, localhost aponta para o próprio contêiner, não para o host. Isso faz parte do isolamento por namespace de rede do Docker; não é um bug.

2. Escolha o método adequado

Use a opção apropriada para seu ambiente:

AmbienteOpção recomendadaConfiguração
macOS/Windows com Docker DesktopUsar host.docker.internal diretamenteNenhuma configuração adicional
Linux com Docker Engine 20.10+extra_hosts: host-gatewayDocker Compose ou —add-host
Equipe multiplataformaextra_hosts: host-gatewayUma configuração para todas as plataformas
Versão antiga do Docker no LinuxUsar 172.17.0.1Definir o IP em extra_hosts
Se nada mais funcionar--network=hostApenas para desenvolvimento local; remove o isolamento

3. Configure também o serviço

Configurar o contêiner não basta. O serviço no host também precisa estar preparado:

  • Altere o endereço de escuta para 0.0.0.0
  • Conceda ao usuário do MySQL acesso a partir da rede Docker
  • Permita o acesso da rede Docker no firewall

Árvore rápida de decisão

Quando a conexão falhar:

Não consegue acessar o serviço do host?

Você usa macOS/Windows ou Linux?

macOS/Windows:
  → Use host.docker.internal diretamente
  → Se ainda falhar, verifique a configuração do serviço no host

Linux:
  → A versão do Docker é ≥20.10?
      Sim → Use extra_hosts: host-gateway
      Não → Use extra_hosts: 172.17.0.1
  → Verifique a configuração do serviço no host
  → Verifique o firewall

Ainda não funciona?
  → Siga cada item do checklist de diagnóstico
  → Última alternativa: --network=host, somente para desenvolvimento local

Para finalizar

Desenvolver com contêineres é muito prático, mas a parte de rede tem algumas armadilhas. Depois que você entende host.docker.internal e host-gateway, porém, consegue resolver a maioria desses problemas.

Salve este artigo para consultar quando uma conexão falhar novamente. Se alguém da sua equipe estiver enfrentando a mesma dificuldade, compartilhe este guia.

E se você já encontrou algum problema estranho de rede no Docker ou conhece uma solução melhor, conte nos comentários. Sua experiência pode ajudar outras pessoas.

Processo completo para acessar o host a partir de um contêiner Docker

Use host.docker.internal para acessar serviços do host a partir de contêineres no macOS, Windows e Linux.

⏱️ Estimated time: 15 min

  1. 1

    Step 1: Entenda a origem do problema e a solução

    Origem do problema: usar localhost ou 127.0.0.1 dentro do contêiner para conectar a um serviço do host sempre falha, porque localhost aponta para o próprio contêiner. É preciso uma configuração específica para acessar os serviços do host.

    Solução: use o domínio especial host.docker.internal, que é resolvido automaticamente para o endereço IP do host.

    Suporte por plataforma:
    • macOS/Windows: suporte nativo no Docker Desktop, sem configuração adicional
    • Linux: requer Docker 20.10+ e o parâmetro --add-host ou uma configuração no Docker Compose
  2. 2

    Step 2: Configure no macOS e no Windows

    Etapas no macOS e no Windows:

    1. Use host.docker.internal diretamente:
    docker run -e DATABASE_URL=host.docker.internal:3306 my-app

    2. Substitua localhost na configuração da aplicação:
    • String de conexão: host.docker.internal:3306
    • Variável de ambiente: DATABASE_HOST=host.docker.internal

    3. Valide a conexão:
    • Teste dentro do contêiner: docker exec -it container-name ping host.docker.internal
    • Teste a conexão da aplicação diretamente

    Observação: macOS e Windows não exigem configuração adicional; o Docker Desktop cuida disso automaticamente.
  3. 3

    Step 3: Configure e diagnostique no Linux

    Métodos de configuração no Linux:

    1. Docker versão ≥20.10 (recomendado):
    docker run --add-host=host.docker.internal:host-gateway my-app

    Ou, no docker-compose.yml:
    extra_hosts:
    - "host.docker.internal:host-gateway"

    2. Docker versão <20.10:
    docker run --add-host=host.docker.internal:172.17.0.1 my-app

    3. Checklist completo de diagnóstico:
    • Confirme que o serviço está em execução no host (netstat -tuln | grep 3306)
    • Verifique se a porta está aberta (telnet host.docker.internal 3306)
    • Use host.docker.internal no lugar de localhost
    • No Linux, adicione o parâmetro --add-host
    • Verifique as regras do firewall (iptables -L)
    • Confirme que o serviço escuta em 0.0.0.0, e não em 127.0.0.1

FAQ

Por que localhost não conecta o contêiner a um serviço do host?
Por causa do isolamento de rede: cada contêiner tem seu próprio namespace de rede, e localhost dentro dele aponta para o próprio contêiner (127.0.0.1), não para o host.

É como duas casas independentes: embora estejam na mesma máquina física, cada uma tem seu próprio endereço de rede. Ao acessar localhost, o contêiner procura dentro da própria rede, não na rede do host.

A solução é usar o domínio especial host.docker.internal, que é resolvido automaticamente para o endereço IP do host e permite que o contêiner acesse os serviços executados nele.
host.docker.internal funciona em todas as plataformas?
Suporte por plataforma:

• macOS/Windows (Docker Desktop): suporte nativo, sem configuração
• Linux (Docker 20.10+): requer configuração manual com --add-host
• Linux (Docker <20.10): requer o IP 172.17.0.1 como endereço do host

Para equipes multiplataforma, recomenda-se padronizar extra_hosts no Docker Compose, para que a mesma configuração funcione em todos os sistemas.
O que fazer se host.docker.internal estiver configurado, mas a conexão ainda falhar?
Etapas de diagnóstico:

1. Verifique se o serviço está em execução no host:
netstat -tuln | grep número_da_porta

2. Confirme o endereço em que o serviço está escutando:
O serviço precisa escutar em 0.0.0.0, não apenas em 127.0.0.1

3. Teste a conectividade da rede:
docker exec -it container-name ping host.docker.internal
docker exec -it container-name telnet host.docker.internal número_da_porta

4. Verifique as regras do firewall:
iptables -L (Linux)
Configurações do Firewall do Windows

5. Última alternativa, somente para desenvolvimento:
--network=host (remove o isolamento de rede do contêiner)
Devo usar host.docker.internal em produção?
Não é recomendado. Em produção, prefira:

• Nomes de serviço na rede do Docker Compose ou nomes de contêiner
• Endereços IP explícitos
• Mecanismos de descoberta de serviços, como Consul ou etcd

host.docker.internal é indicado principalmente para:
• Ambientes locais de desenvolvimento
• Depuração e testes
• Acesso a ferramentas de desenvolvimento executadas no host, como bancos de dados e Redis

Usar host.docker.internal em produção pode:
• Criar dependência da configuração de rede do host
• Reduzir as vantagens da conteinerização
• Aumentar a complexidade operacional

17 min de leitura · Publicado em: 17 dez 2025 · Atualizado em: 4 set 2026

Comentários

Entre com GitHub para comentar

Easton BlogEaston Blog