Acceso del contenedor Docker al host: guía completa de host.docker.internal

Un viernes a las tres de la tarde miraba el terminal: Connection refused.
La verdad es que me desesperé. MySQL local funcionaba bien, Navicat conectaba, la línea de comandos también, pero la app en el contenedor no. Revisé tres veces la cadena de conexión: localhost:3306, correcta. Usuario y contraseña también. ¿Dónde estaba el problema?
Al final descubrí que estaba en una sola palabra: localhost.
Si te ha pasado lo mismo — usar localhost o 127.0.0.1 dentro de un contenedor Docker para conectar con un servicio del host y que siempre falle — este artículo es para ti. Te explico de forma directa por qué localhost dentro del contenedor no es el localhost que imaginas, y cómo resolverlo con el dominio mágico host.docker.internal.
En este artículo aprenderás:
- La lógica real del aislamiento de red del contenedor (sin jerga innecesaria)
- La configuración correcta en Mac, Windows y Linux
- Una lista práctica de diagnóstico para la próxima vez
¿Por qué localhost no funciona?
Respuesta corta: el contenedor tiene su propio mundo de red.
Suena abstracto. Piensa en el contenedor como una casita independiente con su propio número, buzón y todo lo demás. Cuando dentro del contenedor dices «localhost» o llamas a 127.0.0.1, buscas esa casita, no la máquina anfitriona de fuera.
En concreto:
- En el host,
localhostapunta al propio host - En el contenedor,
localhostapunta al propio contenedor - Son dos
localhostdistintos
La primera vez que lo entendí también me sorprendió. MySQL corría perfectamente en mi ordenador; ¿por qué el contenedor no conectaba? Por eso: el contenedor busca MySQL en su propio mundo y no lo encuentra.
Mecanismo de aislamiento de red del contenedor
Docker crea un espacio de nombres de red independiente para cada contenedor (no te asustes con el término). En la práctica:
Cada contenedor tiene su propia interfaz de red, su IP y su tabla de enrutamiento. Como dos pisos en el mismo edificio: cada uno con su Wi‑Fi, sin interferencias.
Contenedor y host se conectan mediante un puente virtual llamado docker0. La IP del contenedor suele ser 172.17.0.x; desde el contenedor, el host se ve como 172.17.0.1 (la puerta de enlace del puente).
Si dentro del contenedor accedes a localhost, llegas al 127.0.0.1 del contenedor, no al del host. Por eso no conecta con MySQL del host.
Un error típico:
Error: connect ECONNREFUSED 127.0.0.1:3306
O:
Can't connect to MySQL server on 'localhost' (111)
Son el clásico fallo de «usar localhost en el contenedor para llegar al host».
¿Qué es host.docker.internal?
Si localhost no sirve, ¿cómo accede el contenedor al host?
Docker ofrece una solución elegante: host.docker.internal. Es un dominio especial que se resuelve a la IP del host. Piénsalo como el apodo del host: da igual cuál sea su IP real, con ese nombre lo encuentras.
Por ejemplo, si MySQL escucha en el puerto 3306 del host, desde el contenedor conectas así:
mysql://user:[email protected]:3306/dbname
No importa si el host es 192.168.1.100 o 10.0.0.5, ni si cambias de red: host.docker.internal apunta a la dirección correcta.
¿Cómodo, verdad?
Versiones y soporte por plataforma
Aquí hay un matiz importante.
Usuarios de Mac y Windows (Docker Desktop)
Con Docker Desktop (la versión con interfaz gráfica), desde la versión 18.03 (marzo de 2018) host.docker.internal viene soportado de forma nativa. Listo para usar, sin configuración extra.
En el código escribes directamente host.docker.internal:
const mysql = require('mysql2');
const connection = mysql.createConnection({
host: 'host.docker.internal', // Así de simple
port: 3306,
user: 'root',
password: 'your_password'
});
Usuarios de Linux (Docker Engine)
En Linux la cosa es distinta. Docker corre directamente sobre el sistema, sin la capa de máquina virtual de Mac/Windows, y host.docker.internal no existe por defecto.
Desde Docker Engine 20.10 (diciembre de 2020) puedes habilitarlo con configuración. Cómo hacerlo lo vemos en la siguiente sección.
Si tu versión es más antigua, alternativas:
- Usar
172.17.0.1(IP de puerta de enlace del puente por defecto) - Usar la IP real del host en la red Docker
- Usar
docker.for.mac.host.internal(solo en versiones antiguas de Mac)
Configuración en las tres plataformas
Aquí va lo práctico: configuraciones que puedes copiar.
Mac/Windows (Docker Desktop)
El caso más sencillo.
Método 1: directamente en el código
Sin configuración extra; en el código usa host.docker.internal:
# docker-compose.yml
version: '3'
services:
app:
image: myapp:latest
environment:
- DB_HOST=host.docker.internal # Directo
- DB_PORT=3306
Método 2: declaración explícita (opcional)
Aunque no hace falta, puedes añadir extra_hosts:
version: '3'
services:
app:
image: myapp:latest
extra_hosts:
- "host.docker.internal:host-gateway"
environment:
- DB_HOST=host.docker.internal
host-gateway es la sintaxis nueva de Docker 20.10+ y significa «dirección de puerta de enlace del host».
Con docker run:
docker run -d \
--add-host=host.docker.internal:host-gateway \
-e DB_HOST=host.docker.internal \
myapp:latest
Linux (Docker Engine)
En Linux hay un paso manual más.
Método 1: recomendado — host-gateway
El enfoque más universal, válido en Docker 20.10+ en todas las plataformas:
# docker-compose.yml
version: '3'
services:
app:
image: myapp:latest
extra_hosts:
- "host.docker.internal:host-gateway" # Configuración clave
environment:
- DB_HOST=host.docker.internal
- DB_PORT=3306
Con docker run:
docker run -d \
--add-host=host.docker.internal:host-gateway \
-e DB_HOST=host.docker.internal \
myapp:latest
Ventaja: multiplataforma — la misma configuración en Mac, Windows y Linux.
Método 2: alternativa — IP del puente Docker
Si host-gateway no está disponible (Docker muy antiguo), usa la puerta de enlace del puente por defecto:
version: '3'
services:
app:
image: myapp:latest
extra_hosts:
- "host.docker.internal:172.17.0.1" # Puerta de enlace por defecto
environment:
- DB_HOST=host.docker.internal
172.17.0.1 es la puerta de enlace de la red bridge de Docker. En casi todos los casos es correcta, salvo que hayas cambiado la red por defecto.
Método 3: último recurso — modo host
Si nada más funciona:
docker run -d \
--network=host \
-e DB_HOST=localhost \ # Aquí sí puedes usar localhost
myapp:latest
O en docker-compose:
version: '3'
services:
app:
image: myapp:latest
network_mode: "host" # Red del host
environment:
- DB_HOST=localhost # localhost real
Ventajas: directo; el contenedor usa la pila de red del host y localhost es localhost de verdad.
Inconvenientes:
- Rompe el aislamiento de red del contenedor
- Contenedor y host comparten puertos (posibles conflictos, p. ej. 8080)
- Solo en Linux; Mac/Windows no lo soportan
- No recomendado en producción; solo desarrollo local
Configuración multiplataforma (muy recomendada)
Si en el equipo hay Mac y Linux, o despliegas en entornos distintos, usa esto:
# docker-compose.yml
version: '3'
services:
app:
image: myapp:latest
extra_hosts:
- "host.docker.internal:host-gateway" # Todas las plataformas lo entienden
environment:
- DB_HOST=host.docker.internal
- DB_PORT=3306
- DB_USER=root
- DB_PASSWORD=your_password
Funciona en Docker 20.10+ (finales de 2020) en todas las plataformas. Si tu Docker es anterior… en serio, toca actualizar.
Puntos clave de configuración en el host
Configurar el contenedor no basta.
El servicio en el host también debe estar bien; mucha gente lo olvida.
El servicio debe escuchar en la dirección correcta
Es el fallo más habitual.
Muchos servicios escuchan solo en 127.0.0.1, es decir, solo conexiones locales. Un contenedor Docker no cuenta como «local»; las peticiones desde el puente Docker se rechazan.
Hay que escuchar en 0.0.0.0: «aceptar conexiones en todas las interfaces».
Configuración de MySQL
Archivo de configuración, normalmente en:
- Linux:
/etc/mysql/mysql.conf.d/mysqld.cnf - Mac (Homebrew):
/usr/local/etc/my.cnf - Windows:
C:\ProgramData\MySQL\MySQL Server 8.0\my.ini
Modifica bind-address:
[mysqld]
# Antes podía ser:
# bind-address = 127.0.0.1
# Cámbialo a:
bind-address = 0.0.0.0
Reinicia MySQL:
# Linux
sudo systemctl restart mysql
# Mac
brew services restart mysql
# Windows
# Reinicia el servicio MySQL desde el administrador de servicios
Configuración de Redis
Edita redis.conf (suele estar en /etc/redis/redis.conf o /usr/local/etc/redis.conf):
# Busca esta línea
bind 127.0.0.1 -::1
# Cámbiala a
bind 0.0.0.0
Reinicia Redis:
# Linux
sudo systemctl restart redis
# Mac
brew services restart redis
Configuración de PostgreSQL
Edita postgresql.conf:
listen_addresses = '*' # Escuchar en todas las direcciones
Y modifica pg_hba.conf para permitir la red Docker:
# Añade esta línea para la red 172.17.0.0/16
host all all 172.17.0.0/16 md5
Permisos de usuario (MySQL)
Aunque MySQL escuche en 0.0.0.0, siguen los permisos de usuario.
En MySQL los permisos son «usuario@host de origen». root@localhost y root@% no son lo mismo.
Si el usuario solo puede conectar desde localhost, el contenedor seguirá fallando. Concede acceso desde la red Docker:
-- Opción 1: desde cualquier host (simple, menos seguro)
GRANT ALL PRIVILEGES ON *.* TO 'your_user'@'%' IDENTIFIED BY 'your_password';
-- Opción 2: solo red Docker (más seguro)
GRANT ALL PRIVILEGES ON *.* TO 'your_user'@'172.17.0.%' IDENTIFIED BY 'your_password';
-- Aplicar permisos
FLUSH PRIVILEGES;
En MySQL 8.0+ la sintaxis cambia un poco:
-- Crear usuario
CREATE USER 'your_user'@'%' IDENTIFIED BY 'your_password';
-- Conceder permisos
GRANT ALL PRIVILEGES ON *.* TO 'your_user'@'%';
FLUSH PRIVILEGES;
Configuración del firewall
En algunos sistemas el firewall bloquea que los contenedores accedan a servicios del host.
Comprobar estado del firewall:
# Linux (ufw)
sudo ufw status
# Linux (firewalld)
sudo firewall-cmd --state
Permitir la red Docker (ejemplo puerto 3306 de MySQL):
# 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
Recomendaciones de seguridad
Escuchar en 0.0.0.0 tiene riesgo: el servicio queda expuesto a otras máquinas de la red.
En producción:
- Solo una interfaz concreta: si sabes qué interfaz usa Docker, escucha solo ahí
bind-address = 172.17.0.1
2. **Firewall**: permite solo la red Docker y bloquea el resto
3. **Base de datos en contenedor**: no corras la BD en el host; levántala con Docker Compose en la misma red que la app
**En desarrollo local**:
En local, escuchar en `0.0.0.0` suele ser aceptable. Tu portátil no es un servidor expuesto a Internet. No te agobies.
## Lista de diagnóstico de problemas frecuentes
¿Problemas de conexión? Sigue esta lista paso a paso.
### Problema 1: Connection refused (conexión rechazada)
El error más común:
Error: connect ECONNREFUSED host.docker.internal:3306
O:
Can’t connect to MySQL server on ‘host.docker.internal’ (111)
**Causas posibles y comprobaciones**:
**Paso 1: ¿el servicio corre en el host?**
En el host:
```bash
# MySQL
sudo systemctl status mysql # Linux
brew services list # Mac
# ¿Escucha el puerto?
netstat -an | grep 3306
# o
lsof -i :3306
Si no está activo, arranca el servicio.
Paso 2: dirección de escucha
En el host:
# Dónde escucha MySQL
sudo netstat -tlnp | grep 3306
La salida debería ser similar a:
tcp 0 0 0.0.0.0:3306 0.0.0.0:* LISTEN 1234/mysqld
Mira la tercera columna. 0.0.0.0:3306 = todas las interfaces, bien. 127.0.0.1:3306 = solo local, el contenedor no entra.
Solución: en la sección de configuración del host, pon bind-address = 0.0.0.0.
Paso 3: firewall
Prueba desactivarlo temporalmente:
# Linux (ufw)
sudo ufw disable
# Linux (firewalld)
sudo systemctl stop firewalld
# Mac
# Preferencias del sistema → Seguridad → Firewall → Desactivar
Si al desactivarlo conecta, configura reglas como arriba y vuelve a activar el firewall.
Problema 2: Connection timeout (tiempo de espera agotado)
Error: connect ETIMEDOUT host.docker.internal:3306
El timeout suele ser más difícil: el paquete sale pero no vuelve.
Comprobaciones:
Paso 1: ¿se resuelve host.docker.internal?
Dentro del contenedor:
# Entrar al contenedor
docker exec -it your_container sh
# Probar
ping host.docker.internal
Si falla o dice «unknown host», host.docker.internal no está bien configurado.
En Linux: confirma extra_hosts o --add-host=host.docker.internal:host-gateway en docker-compose o docker run.
Paso 2: ¿puerto correcto?
¿Seguro que es 3306? ¿MySQL usa otro puerto?
En el host:
# Puerto real de MySQL
sudo netstat -tlnp | grep mysqld
Paso 3: conectividad contenedor → host
Dentro del contenedor:
# Probar puerto
telnet host.docker.internal 3306
# Sin telnet, usa nc
nc -zv host.docker.internal 3306
Si el puerto no responde, revisa firewall y configuración del servicio.
Problema 3: Unknown host (no resuelve host.docker.internal)
getaddrinfo ENOTFOUND host.docker.internal
Fallo de DNS: el contenedor no conoce ese dominio.
Solución:
Añade extra_hosts:
services:
app:
extra_hosts:
- "host.docker.internal:host-gateway"
O en docker run:
docker run --add-host=host.docker.internal:host-gateway ...
Problema 4: Error de autenticación (Access denied)
Access denied for user 'root'@'172.17.0.2' (using password: YES)
Ya conecta con MySQL, pero los permisos no cuadran.
Solución:
Concede permisos al usuario:
-- Ver usuarios actuales
SELECT user, host FROM mysql.user WHERE user='root';
-- Si solo existe root@localhost, crea root@% o [email protected].%
CREATE USER 'root'@'%' IDENTIFIED BY 'your_password';
GRANT ALL PRIVILEGES ON *.* TO 'root'@'%';
FLUSH PRIVILEGES;
Problema 5: Configuración distinta entre plataformas
Mismo docker-compose.yml: en Mac funciona, en Linux no.
Solución:
Unifica con host-gateway:
services:
app:
extra_hosts:
- "host.docker.internal:host-gateway"
Docker ≥20.10. Si alguien del equipo tiene una versión antigua, que actualice.
Regla mnemotécnica rápida
Orden de comprobación:
- ¿Servicio activo? →
systemctl status/brew services list - ¿Escucha bien? →
netstat -tlnp, ¿0.0.0.0o127.0.0.1? - ¿Contenedor configurado? →
extra_hostso--add-host - ¿DNS OK? →
ping host.docker.internaldentro del contenedor - ¿Puerto abierto? →
telnetoncdesde el contenedor - ¿Firewall? → desactivar temporalmente para probar
- ¿Permisos MySQL? → usuario
@localhostvs@%
En nueve de diez casos es uno de los tres primeros.
Casos prácticos
Teoría aparte, dos ejemplos reales.
Caso 1: Spring Boot conectando a MySQL en el host
Escenario: proyecto Spring Boot en Docker, MySQL local en el host.
Paso 1: configurar Spring Boot
application.yml:
spring:
datasource:
# MySQL del host con host.docker.internal
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
Paso 2: Docker Compose
docker-compose.yml:
version: '3.8'
services:
app:
build: .
ports:
- "8080:8080"
extra_hosts:
- "host.docker.internal:host-gateway" # Clave
environment:
SPRING_DATASOURCE_URL: jdbc:mysql://host.docker.internal:3306/mydb
SPRING_DATASOURCE_USERNAME: root
SPRING_DATASOURCE_PASSWORD: your_password
Paso 3: MySQL en el host
Edita /etc/mysql/mysql.conf.d/mysqld.cnf:
[mysqld]
bind-address = 0.0.0.0
Reinicia MySQL:
sudo systemctl restart mysql
Concede permisos:
CREATE USER 'root'@'%' IDENTIFIED BY 'your_password';
GRANT ALL PRIVILEGES ON *.* TO 'root'@'%';
FLUSH PRIVILEGES;
Paso 4: arrancar y probar
docker-compose up --build
Si ves algo como HikariPool-1 - Start completed, la base de datos conectó.
Diagnóstico real:
La primera vez tuve Connection refused:
- ¿MySQL activo?
systemctl status mysql→ sí - ¿Dónde escucha?
netstat -tlnp | grep 3306→127.0.0.1:3306 - Cambié
bind-address = 0.0.0.0, reinicié MySQL - Volví a ejecutar → conectó
Caso 2: Node.js con Redis en el host
Escenario: proyecto Node.js con Redis como caché; en desarrollo Redis corre en el host.
Paso 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,
// Si Redis tiene contraseña
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;
Paso 2: Docker Compose
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
Paso 3: Redis en el host
Edita /etc/redis/redis.conf o /usr/local/etc/redis.conf:
# Línea bind
bind 127.0.0.1 ::1
# Cámbiala a
bind 0.0.0.0
Si protected-mode yes:
protected-mode no # OK en desarrollo local; no en producción
Reinicia Redis:
# Linux
sudo systemctl restart redis
# Mac
brew services restart redis
Paso 4: verificar
docker-compose up
Si aparece Redis connected successfully, listo.
Multiplataforma:
Con variables de entorno unificadas:
const REDIS_HOST = process.env.REDIS_HOST || (
process.platform === 'linux' ? 'host.docker.internal' : 'host.docker.internal'
);
Espera — ahora todos pueden usar host.docker.internal. Solo hace falta extra_hosts: ["host.docker.internal:host-gateway"] en Compose; Mac y Linux comparten la misma configuración.
Caso 3: entorno de desarrollo completo
Plantilla útil: app en contenedor, MySQL y Redis en el host:
# docker-compose.yml
version: '3.8'
services:
app:
build: .
ports:
- "8080:8080"
extra_hosts:
- "host.docker.internal:host-gateway"
environment:
# Base de datos
DB_HOST: host.docker.internal
DB_PORT: 3306
DB_NAME: myapp
DB_USER: root
DB_PASSWORD: your_password
# Redis
REDIS_HOST: host.docker.internal
REDIS_PORT: 6379
# App
NODE_ENV: development
PORT: 8080
volumes:
- .:/app
- /app/node_modules # No montar node_modules
command: npm run dev # Recarga en caliente
Checklist en el host:
# MySQL
# Editar /etc/mysql/mysql.conf.d/mysqld.cnf
bind-address = 0.0.0.0
# Reinicio: sudo systemctl restart mysql
# Redis
# Editar /etc/redis/redis.conf
bind 0.0.0.0
protected-mode no
# Reinicio: sudo systemctl restart redis
# Firewall (si hace falta)
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
Esta configuración sirve en Mac y Linux; copia y pega.
Resumen
En tres ideas:
1. Entender el principio
El contenedor tiene su propia red; localhost dentro del contenedor es el contenedor, no el host. Es aislamiento por espacios de nombres de red, diseño de Docker, no un bug.
2. Elegir el método
Según tu entorno:
| Entorno | Enfoque recomendado | Configuración |
|---|---|---|
| Mac/Windows (Docker Desktop) | Usar host.docker.internal directamente | Sin configuración extra |
| Linux (Docker Engine 20.10+) | extra_hosts: host-gateway | docker-compose o —add-host |
| Equipo multiplataforma | extra_hosts: host-gateway | Una sola configuración |
| Linux antiguo | 172.17.0.1 | extra_hosts con IP fija |
| Último recurso | --network=host | Solo desarrollo local; rompe aislamiento |
3. Configurar bien el host
El contenedor no basta; en el host:
- Escuchar en
0.0.0.0 - Permisos MySQL para la red Docker
- Firewall que permita la red Docker
Árbol de decisión rápido
Si no conecta al host:
¿No conecta al servicio del host?
↓
¿Mac/Windows o Linux?
↓
Mac/Windows:
→ Usa host.docker.internal
→ Si falla, revisa configuración del servicio en el host
Linux:
→ ¿Docker ≥20.10?
Sí → extra_hosts: host-gateway
No → extra_hosts: 172.17.0.1
→ Revisa servicio en el host
→ Revisa firewall
¿Sigue fallando?
→ Lista de diagnóstico paso a paso
→ Último recurso: --network=host (solo desarrollo local)
Para cerrar
Desarrollar con contenedores es cómodo, pero la red tiene trampas. Con host.docker.internal y host-gateway resuelves la mayoría.
Guarda este artículo para la próxima vez. Si alguien del equipo sufre lo mismo, compártelo.
Si has visto algún caso raro de red en contenedores o tienes otra solución, cuéntalo en los comentarios; puede ayudar a más gente.
Flujo completo de configuración para que un contenedor Docker acceda al host
Usa host.docker.internal para resolver el acceso del contenedor a servicios del host en Mac, Windows y Linux
⏱️ Estimated time: 15 min
- 1
Step 1: Entender la causa y la solución
Causa raíz: conectar al host con localhost o 127.0.0.1 desde el contenedor siempre falla, porque localhost dentro del contenedor apunta al propio contenedor, no al host; hace falta una configuración especial.
Solución: usa el dominio mágico host.docker.internal, que se resuelve automáticamente a la IP del host.
Soporte por plataforma:
• Mac/Windows: Docker Desktop lo soporta de forma nativa, sin configuración extra
• Linux: requiere Docker 20.10+, con --add-host o extra_hosts en docker-compose - 2
Step 2: Configuración en Mac/Windows
Pasos en Mac/Windows:
1. Usa host.docker.internal directamente:
docker run -e DATABASE_URL=host.docker.internal:3306 my-app
2. Sustituye localhost en la configuración de la app:
• Cadena de conexión: host.docker.internal:3306
• Variable de entorno: DATABASE_HOST=host.docker.internal
3. Verifica la conexión:
• Prueba dentro del contenedor: docker exec -it container-name ping host.docker.internal
• Prueba la conexión de la aplicación
Nota: en Mac/Windows no hace falta configuración extra; Docker Desktop lo gestiona automáticamente. - 3
Step 3: Configuración y diagnóstico en Linux
Configuración en Linux:
1. Docker ≥20.10 (recomendado):
docker run --add-host=host.docker.internal:host-gateway my-app
O en docker-compose.yml:
extra_hosts:
- "host.docker.internal:host-gateway"
2. Docker <20.10:
docker run --add-host=host.docker.internal:172.17.0.1 my-app
3. Lista de comprobación:
• Confirma que el servicio corre en el host (netstat -tuln | grep 3306)
• Comprueba que el puerto está abierto (telnet host.docker.internal 3306)
• Usa host.docker.internal en lugar de localhost
• En Linux añade --add-host
• Revisa el firewall (iptables -L)
• Confirma que el servicio escucha en 0.0.0.0 y no solo en 127.0.0.1
FAQ
¿Por qué localhost desde el contenedor no conecta con servicios del host?
Es como dos casas independientes en la misma máquina física, cada una con su dirección de red. Al acceder a localhost, el contenedor usa su red interna, no la del host.
Solución: usa el dominio especial host.docker.internal, que se resuelve a la IP del host y permite acceder a servicios que corren en él.
¿host.docker.internal está soportado en todas las plataformas?
• Mac/Windows (Docker Desktop): soporte nativo, sin configuración
• Linux (Docker 20.10+): hay que configurar --add-host manualmente
• Linux (Docker <20.10): usa 172.17.0.1 como IP del host
Para equipos multiplataforma: unifica con extra_hosts en docker-compose para que funcione en todas las plataformas.
¿Qué hacer si sigue sin conectar tras configurar host.docker.internal?
1. Comprueba que el servicio corre en el host:
netstat -tuln | grep <puerto>
2. Confirma la dirección de escucha:
el servicio debe escuchar en 0.0.0.0, no solo en 127.0.0.1
3. Prueba la conectividad:
docker exec -it container-name ping host.docker.internal
docker exec -it container-name telnet host.docker.internal <puerto>
4. Revisa el firewall:
iptables -L (Linux)
Configuración del firewall de Windows
5. Último recurso (solo desarrollo):
--network=host (rompe el aislamiento del contenedor)
¿Debería usarse host.docker.internal en producción?
• Usar nombres de servicio (red Docker Compose) o nombres de contenedor
• Usar direcciones IP explícitas
• Usar descubrimiento de servicios (Consul, etcd, etc.)
host.docker.internal sirve sobre todo para:
• Entorno de desarrollo local
• Depuración y pruebas
• Acceder a herramientas de desarrollo en el host (base de datos, Redis)
En producción implica:
• Depender de la red del host
• Reducir las ventajas de la contenedorización
• Aumentar la complejidad operativa
13 min de lectura · Publicado el: 17 dic 2025 · Actualizado el: 21 ago 2026
Guía práctica de Docker
Si llegaste desde búsqueda, lo más rápido es ir al artículo anterior o siguiente de esta misma serie.
Anterior
Mapeo de puertos en Docker: no dejes que «puerto ya asignado» arruine tu viernes por la noche
Desde el diagnóstico de puertos ocupados hasta la optimización del rendimiento: resuelve de forma sistemática todos los dolores del mapeo de puertos en Docker y despídete del error port already allocated
Parte 20 de 38
Siguiente
Modos de red Docker en la práctica: guía para elegir entre bridge, host y overlay
Comparación detallada del rendimiento, los casos de uso y la configuración de los tres modos de red Docker, con diagrama de decisión y datos de prueba. Bridge por defecto en un solo host, Host para rendimiento, Overlay para comunicación entre hosts.
Parte 22 de 38



Comentarios
Inicia sesión con GitHub para dejar un comentario