Código Rápido
Si solo necesitas el código para configurar tu reverse proxy con Nginx y certificados SSL automáticos, aquí está:
services:
nginx-proxy:
image: nginxproxy/nginx-proxy
container_name: nginx-proxy
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
- certs:/etc/nginx/certs
- vhost:/etc/nginx/vhost.d
- html:/usr/share/nginx/html
networks:
- proxy
restart: always
letsencrypt:
image: nginxproxy/acme-companion
container_name: nginx-proxy-letsencrypt
volumes_from:
- nginx-proxy
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- acme:/etc/acme.sh
environment:
- DEFAULT_EMAIL=tu-email@ejemplo.com
depends_on:
- nginx-proxy
networks:
- proxy
restart: always
volumes:
certs:
vhost:
html:
acme:
networks:
proxy:
external: true
Para usar con tus aplicaciones, agrega estas variables de entorno:
services:
tu-aplicacion:
image: tu-imagen
environment:
- VIRTUAL_HOST=tudominio.com
- LETSENCRYPT_HOST=tudominio.com
networks:
- proxy
Nota: Asegúrate de crear la red proxy antes de iniciar los contenedores:
docker network create proxy
Introducción
Un reverse proxy es una herramienta esencial cuando manejas múltiples aplicaciones en Docker. En lugar de recordar diferentes puertos para cada servicio (como :8080, :3000, etc.), un reverse proxy te permite acceder a tus aplicaciones usando dominios o subdominios normales.
En este tutorial, vamos a usar nginx-proxy, una solución automatizada que:
- Detecta automáticamente tus contenedores Docker
- Configura rutas basadas en nombres de dominio
- Genera certificados SSL automáticamente con Let's Encrypt
- Renueva los certificados antes de que expiren
¿Qué es nginx-proxy?
nginx-proxy es una imagen de Docker que ejecuta Nginx y genera automáticamente configuraciones de reverse proxy para otros contenedores Docker utilizando docker-gen. La magia ocurre porque monitorea el socket de Docker y detecta cuándo inicias o detienes contenedores.
Cuando un contenedor tiene la variable de entorno VIRTUAL_HOST, nginx-proxy automáticamente:
- Crea una configuración de Nginx para ese host
- Redirige el tráfico del dominio hacia ese contenedor
- Actualiza la configuración si reinicias el contenedor
Contenedores necesarios
1. nginx-proxy (nginxproxy/nginx-proxy)
Este es el contenedor principal que maneja todo el tráfico HTTP/HTTPS entrante.
Puertos expuestos:
-
80:80— Tráfico HTTP estándar -
443:443— Tráfico HTTPS cifrado
Volúmenes importantes:
-
/var/run/docker.sock:/tmp/docker.sock:ro— Acceso de solo lectura al socket de Docker para detectar contenedores -
certs:/etc/nginx/certs— Almacena los certificados SSL -
vhost:/etc/nginx/vhost.d— Configuraciones personalizadas por virtual host -
html:/usr/share/nginx/html— Archivos estáticos para validación de Let's Encrypt
2. acme-companion (nginxproxy/acme-companion)
Este contenedor complementario se encarga de obtener y renovar certificados SSL de Let's Encrypt automáticamente.
Nota: Si usas Cloudflare con proxy activado (nube naranja), Cloudflare ya te proporciona certificados SSL. En ese caso, puedes omitir este contenedor y todo sigue funcionando igual.
¿Cómo funciona?
- Detecta contenedores con
LETSENCRYPT_HOST - Solicita certificados SSL para esos dominios
- Configura Nginx para usar HTTPS
- Renueva certificados automáticamente cada 60 días
Volúmenes importantes:
-
volumes_from: nginx-proxy— Comparte los volúmenes del proxy para acceder a certs, vhost y html -
acme:/etc/acme.sh— Persiste el estado de ACME entre reinicios
Nota: Let's Encrypt dio de baja el servicio de notificaciones por email en enero de 2025. La variable DEFAULT_EMAIL sigue siendo requerida para el registro, pero ya no recibirás notificaciones de renovación.
Configuración paso a paso
Paso 1: Crear la red de Docker
Primero, necesitas crear una red externa que conectará el proxy con tus aplicaciones:
docker network create proxy
Esta red permite que los contenedores se comuniquen entre sí de forma segura.
Paso 2: Crear la carpeta para el docker-compose
mkdir -p nginx-proxy
cd nginx-proxy
Paso 3: Crear el archivo docker-compose.yml
Crea un archivo docker-compose.yml con el código mostrado al principio del tutorial.
Importante: Reemplaza tu-email@ejemplo.com con tu email real.
Paso 4: Iniciar el reverse proxy
docker compose up -d
Verifica que los contenedores estén corriendo:
docker ps
Cómo conectar tus aplicaciones
Para que una aplicación use el reverse proxy, solo necesitas agregar las variables de entorno correspondientes y conectarla a la red proxy.
Ejemplo con una aplicación Node.js
services:
mi-api:
image: node:20-alpine
container_name: mi-api
working_dir: /app
volumes:
- ./:/app
command: npm start
environment:
- VIRTUAL_HOST=api.ejemplo.com
- VIRTUAL_PORT=3000
- LETSENCRYPT_HOST=api.ejemplo.com
networks:
- proxy
restart: always
networks:
proxy:
external: true
Explicación de las variables
| Variable | Descripción | Requerida |
|---|---|---|
VIRTUAL_HOST |
El dominio que apuntará a este contenedor | Sí |
VIRTUAL_PORT |
Puerto interno de la aplicación (por defecto 80) | Solo si no es 80 |
LETSENCRYPT_HOST |
Dominio para obtener certificado SSL | Solo con acme-companion |
Múltiples dominios
Si quieres que un contenedor responda a múltiples dominios:
environment:
- VIRTUAL_HOST=app.ejemplo.com,www.app.ejemplo.com
- LETSENCRYPT_HOST=app.ejemplo.com,www.app.ejemplo.com
Esto genera un único certificado válido para ambos dominios.
Ejemplo: Múltiples aplicaciones
Un caso común es tener varias aplicaciones corriendo en el mismo servidor. Aquí un ejemplo con una API, un frontend y una base de datos:
services:
# Backend API
api:
image: node:20-alpine
container_name: mi-api
working_dir: /app
volumes:
- ./api:/app
command: npm start
environment:
- VIRTUAL_HOST=api.ejemplo.com
- VIRTUAL_PORT=3000
- LETSENCRYPT_HOST=api.ejemplo.com
- DATABASE_URL=postgres://user:pass@db:5432/mydb
networks:
- proxy
- internal
restart: always
# Frontend
frontend:
image: node:20-alpine
container_name: mi-frontend
working_dir: /app
volumes:
- ./frontend:/app
command: npm start
environment:
- VIRTUAL_HOST=app.ejemplo.com
- VIRTUAL_PORT=3000
- LETSENCRYPT_HOST=app.ejemplo.com
networks:
- proxy
restart: always
# Base de datos (sin exponer al proxy)
db:
image: postgres:16-alpine
container_name: mi-db
environment:
- POSTGRES_USER=user
- POSTGRES_PASSWORD=pass
- POSTGRES_DB=mydb
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- internal
restart: always
volumes:
postgres_data:
networks:
proxy:
external: true
internal:
driver: bridge
En este ejemplo:
- La API y el frontend están expuestos a través del proxy con sus respectivos dominios
- La base de datos solo está en la red
internal, no accesible desde internet - La API puede comunicarse con la base de datos a través de la red
internal
Conectar una aplicación con su propio docker-compose
Si ya tenés una aplicación con su propio docker-compose.yml y su red interna, podés conectarla al proxy agregando la red proxy como externa:
# docker-compose.yml de tu aplicación existente
services:
app:
image: tu-imagen
environment:
- VIRTUAL_HOST=app.ejemplo.com
- VIRTUAL_PORT=8080
- LETSENCRYPT_HOST=app.ejemplo.com
networks:
- default # red interna de este compose
- proxy # red del reverse proxy
restart: always
redis:
image: redis:alpine
networks:
- default # solo red interna
restart: always
networks:
default:
driver: bridge
proxy:
external: true
Configuración DNS
Para que todo funcione, necesitas apuntar tus dominios a tu servidor. Ejemplo de registro DNS:
| Tipo | Nombre | Valor | TTL |
|---|---|---|---|
| A | api | 203.0.113.50 | 3600 |
| A | app | 203.0.113.50 | 3600 |
Verificación
Verificar logs del proxy
docker logs nginx-proxy
docker logs nginx-proxy-letsencrypt
Verificar que el certificado se generó
docker exec nginx-proxy ls /etc/nginx/certs/
Ver la configuración generada de Nginx
Útil para debuggear problemas:
docker exec nginx-proxy cat /etc/nginx/conf.d/default.conf
Problemas comunes
1. El certificado no se genera
- Verifica que el dominio apunte correctamente a tu servidor con
dig tudominio.com - Asegúrate de que los puertos 80 y 443 estén abiertos en tu firewall
- Revisa los logs:
docker logs nginx-proxy-letsencrypt - Let's Encrypt tiene límites de rate: máximo 5 certificados por dominio por semana
2. Error "502 Bad Gateway"
- Verifica que el contenedor esté en la red
proxy:docker network inspect proxy - Confirma que el contenedor esté corriendo:
docker ps - Si tu app no usa puerto 80, agrega
VIRTUAL_PORT
3. La aplicación usa un puerto diferente al 80
Frameworks como Next.js, NestJS, Express, etc., suelen usar puertos como 3000 u 8080. Especifícalo así:
environment:
- VIRTUAL_HOST=app.ejemplo.com
- VIRTUAL_PORT=3000
Configuraciones avanzadas
Forzar HTTPS (redireccionar HTTP a HTTPS)
Por defecto, nginx-proxy permite tanto HTTP como HTTPS. Para forzar HTTPS y redireccionar todo el tráfico HTTP, agrega esta variable a tu aplicación:
environment:
- VIRTUAL_HOST=app.ejemplo.com
- LETSENCRYPT_HOST=app.ejemplo.com
- HTTPS_METHOD=redirect
Opciones disponibles para HTTPS_METHOD:
| Valor | Comportamiento |
|---|---|
redirect |
Redirecciona HTTP a HTTPS (recomendado) |
noredirect |
Permite HTTP y HTTPS sin redirección |
nohttps |
Solo HTTP, deshabilita HTTPS |
Soporte para WebSockets
Si tu aplicación usa WebSockets (Socket.io, notificaciones en tiempo real, etc.), nginx-proxy los soporta automáticamente. No necesitas configuración adicional.
Sin embargo, si tenés problemas de conexión, podés crear un archivo de configuración para ese host. Crea app.ejemplo.com_location con:
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 86400;
Y móntalo en el proxy:
volumes:
- ./app.ejemplo.com_location:/etc/nginx/vhost.d/app.ejemplo.com_location:ro
Redirección www a non-www (o viceversa)
Para redirigir www.ejemplo.com a ejemplo.com, crea un archivo www.ejemplo.com en la carpeta vhost.d:
return 301 https://ejemplo.com$request_uri;
Y móntalo:
volumes:
- ./www.ejemplo.com:/etc/nginx/vhost.d/www.ejemplo.com:ro
Importante: Asegúrate de que ambos dominios (www.ejemplo.com y ejemplo.com) tengan LETSENCRYPT_HOST configurado para que se generen los certificados.
Cambiar tamaño máximo de subida de archivos
Nginx por defecto limita las subidas a 1 MB. Puedes cambiarlo de forma global o por host.
Para todos los hosts
Crea un archivo my_proxy.conf junto al docker-compose:
client_max_body_size 100m;
Agrégalo como volumen:
services:
nginx-proxy:
# ... resto de la configuración
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
- ./my_proxy.conf:/etc/nginx/conf.d/my_proxy.conf:ro
- certs:/etc/nginx/certs
- vhost:/etc/nginx/vhost.d
- html:/usr/share/nginx/html
Para un host específico
Crea el archivo con el nombre exacto del dominio en /etc/nginx/vhost.d/:
volumes:
- ./my_proxy.conf:/etc/nginx/vhost.d/app.ejemplo.com:ro
Después reinicia con docker compose up -d.
Headers personalizados y configuración de proxy
Para agregar headers o configuraciones específicas de proxy por host, crea un archivo app.ejemplo.com_location:
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_read_timeout 300s;
Y móntalo en /etc/nginx/vhost.d/app.ejemplo.com_location.
Consideraciones de seguridad
El socket de Docker (/var/run/docker.sock) da acceso completo al daemon de Docker. Al montarlo en un contenedor, ese contenedor podría potencialmente controlar otros contenedores. Por eso:
- Se monta como solo lectura (
:ro) - nginx-proxy es una imagen oficial y ampliamente auditada
- En entornos de alta seguridad, considera usar docker-socket-proxy
Conclusión
Con nginx-proxy y acme-companion tienes una solución robusta y automática para manejar múltiples aplicaciones con HTTPS. Lo mejor es que:
- ✅ Los certificados se renuevan automáticamente
- ✅ Agregar nuevas aplicaciones es solo agregar variables de entorno
- ✅ No necesitas configurar Nginx manualmente
- ✅ Todo está contenedorizado y es fácil de mantener
Ahora puedes enfocarte en desarrollar tus aplicaciones sin preocuparte por la configuración del servidor web.
Top comments (0)