Por qué un reverse proxy es imprescindible con Docker
Por defecto, cada contenedor Docker escucha en un puerto arbitrario: su Nextcloud está en el puerto 8080, su Gitea en el 3000, su Vaultwarden en el 8200. Es imposible exponer directamente una decena de puertos en producción: los navegadores no aceptan URL con número de puerto, los certificados TLS cubren nombres de dominio y no puertos, y su cortafuegos debe permanecer cerrado en todo salvo el 80 y el 443. El reverse proxy resuelve estos tres problemas de una sola vez: se convierte en el único punto de entrada público, enruta el tráfico según el nombre de host o la ruta, y se encarga de la terminación TLS. Resultado: app1.su-dominio.com y app2.su-dominio.com apuntan ambas al puerto 443 de su VPS, y el proxy sabe hacia qué contenedor enviar cada petición. Sin este mecanismo, se vería obligado a gestionar manualmente los certificados Let's Encrypt, a abrir múltiples puertos y a complicar considerablemente su cortafuegos.
Lo que un reverse proxy le aporta en la práctica
- Terminación TLS centralizada — una sola herramienta obtiene y renueva automáticamente los certificados Let's Encrypt de todos sus dominios
- Enrutamiento por nombre de host —
git.su-dominio.comva hacia Gitea ycloud.su-dominio.comhacia Nextcloud, sin ningún conflicto de puertos - Cortafuegos simplificado — solo los puertos 80 y 443 están abiertos al público; todos los puertos internos de Docker quedan inaccesibles desde el exterior
- Redirección HTTP → HTTPS automática — todo el tráfico sin cifrar se redirige con un 301, sin configuración manual en cada aplicación
- Observabilidad centralizada — los registros de acceso y de error de todas sus aplicaciones se agregan en un solo lugar para facilitar la depuración
Requisitos comunes a las tres soluciones
Antes de elegir e instalar uno de estos tres proxies, asegúrese de que su VPS cumple algunas condiciones básicas. Docker y Docker Compose deben estar instalados (se recomienda Compose V2, es decir el comando docker compose sin guion). Sus registros DNS deben apuntar a la IP pública de su VPS antes de lanzar la solicitud de certificado: Let's Encrypt comprueba el DNS durante la validación HTTP-01, y un error en esta fase puede provocar un rate limit que bloquee sus intentos durante varias horas. Los puertos 80 y 443 de su VPS deben estar libres: compruebe con ss -tlnp | grep -E ':80|:443' que ningún otro servicio los ocupa ya. Por último, cree una red Docker dedicada para su proxy — docker network create proxy — que conectará a cada contenedor que el proxy deba alcanzar.
Tabla comparativa: Caddy vs Traefik vs Nginx Proxy Manager
Desplace la tabla
| Criterio | Caddy | Traefik | Nginx Proxy Manager |
|---|---|---|---|
| Facilidad de instalación | ⭐⭐⭐⭐⭐ Muy sencilla | ⭐⭐⭐ Media | ⭐⭐⭐⭐⭐ Muy sencilla |
| TLS automático (Let's Encrypt) | ✅ Nativo, cero configuración | ✅ Vía ACME | ✅ Vía interfaz web |
| Integración con Docker | Manual (labels o configuración) | ✅ Labels nativos | ✅ Vía GUI |
| Método de configuración | Archivo Caddyfile legible | Labels Docker + YAML | Interfaz web gráfica |
| Recarga sin cortes | ✅ Automática | ✅ Automática | ✅ Automática |
| Interfaz web/dashboard | ❌ No | ✅ Dashboard incluido | ✅ Interfaz completa |
| Consumo de memoria | ~30 MB | ~50 MB | ~100 MB (MariaDB incluido) |
| Ideal para | Proyectos individuales/equipos pequeños | Microservicios, CI/CD | Principiantes, equipos mixtos |
Caddy: la opción por defecto para la mayoría de los casos
Caddy se ha impuesto como la opción más sencilla para los desarrolladores que alojan entre dos y diez aplicaciones en un VPS. Su filosofía es radical: el TLS se activa por defecto para cualquier dominio válido, sin ninguna opción que marcar ni variable que definir. El archivo de configuración Caddyfile es deliberadamente legible, cercano al lenguaje natural, y puede caber en una decena de líneas para un caso de uso corriente. Caddy está escrito en Go e incorpora su propio cliente ACME: contacta directamente con Let's Encrypt, almacena los certificados en su directorio de datos y los renueva automáticamente antes de que caduquen. A diferencia de Nginx, que necesita certbot como servicio aparte, Caddy no tiene ninguna dependencia externa para el TLS. Su consumo de memoria se mantiene en torno a 30 MB en reposo, lo que lo hace perfectamente adecuado para los VPS de gama de entrada con 1 o 2 GB de RAM. El único defecto real de Caddy: el descubrimiento automático de los contenedores Docker no es nativo. Para remediarlo se usa el plugin caddy-docker-proxy o una configuración estática en el Caddyfile.
Desplegar Caddy como reverse proxy de Docker
Crear la red Docker compartida
Antes de nada, cree la red que Caddy y sus aplicaciones compartirán:
docker network create proxy. Esta red aislada permite a Caddy alcanzar sus contenedores por su nombre sin exponer sus puertos en el host.Escribir el Caddyfile
Cree un archivo
Caddyfileen la raíz de su proyecto. Para exponer una aplicación enapp.su-dominio.comhacia un contenedor llamadomiappque escucha en el puerto 3000:app.su-dominio.com { reverse_proxy miapp:3000 }. Caddy obtiene el certificado automáticamente en el primer arranque.Escribir el docker-compose.yml de Caddy
Cree un
docker-compose.ymlcon el servicio Caddy: monte elCaddyfileen solo lectura (./Caddyfile:/etc/caddy/Caddyfile:ro), persista los datos TLS en un volumen con nombre (caddy_data:/data), publique los puertos 80 y 443, y conecte Caddy a la redproxydeclarándola como red externa.Conectar sus aplicaciones a la red proxy
En el
docker-compose.ymlde cada aplicación, añada la redproxycomo red externa y deje de exponer los puertos en el host (exposeen lugar deports). Caddy alcanzará el contenedor a través de la red interna de Docker.Arrancar y comprobar
Lance Caddy con
docker compose up -dy siga después los registros condocker compose logs -f caddy. Debería vercertificate obtained successfullypara cada dominio. Pruebe concurl -I https://app.su-dominio.com.Añadir una nueva aplicación
Para cada nueva aplicación, añada un bloque en el
Caddyfile, recargue Caddy sin cortes condocker exec caddy caddy reload --config /etc/caddy/Caddyfile, y conecte el nuevo contenedor a la redproxy.
Traefik: cuándo elegir el descubrimiento automático
Traefik destaca en los contextos donde el número de servicios cambia con frecuencia: entornos de CI/CD que crean y destruyen contenedores en cada despliegue, arquitecturas de microservicios con más de cinco servicios independientes, o equipos en los que cada desarrollador despliega sus propios stacks sin tocar una configuración centralizada. Su mecanismo de descubrimiento automático mediante labels de Docker es su principal baza: cuando arranca un contenedor con los labels correctos (traefik.http.routers.miapp.rule=Host('app.su-dominio.com')), Traefik lo detecta al instante y crea la ruta sin que usted tenga que tocar su configuración. Traefik incluye además un dashboard web que muestra en tiempo real todos los routers, servicios y middlewares activos. En cambio, la curva de aprendizaje es más pronunciada: los middlewares, los entrypoints y la sintaxis de los labels pueden desorientar a los principiantes, y una errata en un label puede dejar una aplicación inaccesible de forma silenciosa.
Desplegar Traefik con descubrimiento automático de Docker
Crear el archivo de configuración estática
Cree
traefik.ymlcon los entrypoints (weben el puerto 80,websecureen el 443), active el provider Docker (docker: { exposedByDefault: false }), configure el resolver ACME con su dirección de correo para Let's Encrypt, y active el dashboard en modo seguro.Lanzar Traefik con Docker Compose
En el
docker-compose.ymlde Traefik, monte el socket de Docker en solo lectura (/var/run/docker.sock:/var/run/docker.sock:ro), montetraefik.yml, persista los certificados en un volumen y publique los puertos 80 y 443. Arranque condocker compose up -d.Anotar sus contenedores con labels
En cada servicio que quiera exponer, añada los labels de Traefik:
traefik.enable=true, la rule del router (traefik.http.routers.miapp.rule=Host('app.su-dominio.com')), el entrypoint (websecure), el resolver TLS y el puerto interno del servicio.Comprobar en el dashboard
Acceda al dashboard de Traefik (en el puerto 8080 en local o mediante un subdominio protegido) y compruebe que su router aparece en verde con el estado
Enabled. Si el router no aparece, verifique que el labeltraefik.enable=trueestá presente.
Seguridad crítica: no monte nunca el socket de Docker (/var/run/docker.sock) sin restricciones en un entorno multiusuario, porque cualquiera que pueda escribir en ese socket puede tomar el control total del host. En producción, prefiera el socket en solo lectura (ro) o utilice un proxy de socket como docker-socket-proxy. Aplique también un middleware de autenticación básica al dashboard de Traefik antes de exponerlo públicamente.
Nginx Proxy Manager: la opción sin archivos de configuración
Nginx Proxy Manager (NPM) es la elección evidente para quien no se siente cómodo con los archivos de configuración en línea de comandos. Su interfaz web permite crear un host proxy en unos pocos clics: basta con introducir el nombre de dominio y la dirección del contenedor de destino, marcar «Force SSL» y «HTTP/2 Support» y hacer clic en «Save». NPM se encarga del resto, incluida la solicitud del certificado Let's Encrypt. La interfaz permite además gestionar varios usuarios con permisos distintos, añadir listas de control de acceso y configurar redirecciones. En cambio, NPM incorpora una base de datos MariaDB para almacenar su configuración, lo que eleva su consumo de memoria hasta unos 100 MB, casi el triple que Caddy. NPM encaja perfectamente en equipos mixtos técnicos/no técnicos o en contextos donde otras personas además del sysadmin principal deben gestionar dominios.
Solución de problemas: los errores más frecuentes
La mayoría de los problemas que aparecen pertenece a cuatro categorías. En primer lugar, los rate limits de Let's Encrypt: si relanza su stack varias veces durante las pruebas con el mismo dominio, puede agotar el límite de cinco certificados por dominio cada siete días. Solución: utilice el entorno de staging de Let's Encrypt (acme_ca https://acme-staging-v02.api.letsencrypt.org/directory en Caddy) para sus pruebas. En segundo lugar, los conflictos de puertos: si el puerto 80 o el 443 ya está ocupado por el Apache o el Nginx del host, el proxy Docker no arrancará. Identifique el proceso con ss -tlnp | grep :80. En tercer lugar, los errores de red de Docker: compruebe que el proxy y el contenedor de destino están en la misma red con docker network inspect proxy. En cuarto lugar, en el caso de Traefik, las erratas en los labels: active los registros en modo DEBUG con --log.level=DEBUG.
Resumen: qué proxy para cada perfil
- Desarrollador en solitario, de 2 a 5 aplicaciones — Caddy: configuración mínima, TLS automático y puesta en marcha en menos de diez minutos con un Caddyfile de quince líneas
- Equipo DevOps, microservicios, CI/CD — Traefik: descubrimiento automático mediante labels, dashboard de supervisión, perfecto cuando el número de servicios varía dinámicamente
- Usuario no técnico o equipo mixto — Nginx Proxy Manager: interfaz gráfica intuitiva, ningún archivo que editar, gestión multiusuario integrada
- VPS con poca RAM (512 MB a 1 GB) — Caddy o Traefik: evite NPM, que incorpora MariaDB y consume más memoria
- Migración progresiva desde Nginx — Caddy: su sintaxis se aprende en una hora y puede convivir temporalmente con Nginx en puertos distintos
Conclusión: empiece por Caddy y evolucione si hace falta
Para la gran mayoría de los self-hosters que gestionan unas pocas aplicaciones Docker en un VPS, Caddy es el punto de partida ideal: es sencillo, ligero, opinionado en el buen sentido y gestiona el TLS mejor que cualquier alternativa, sin configuración adicional. Si su infraestructura crece más allá de cinco servicios con despliegues frecuentes y automatizados, Traefik resulta más adecuado gracias a su descubrimiento dinámico. Si tiene que delegar la gestión de los dominios en personas no técnicas, Nginx Proxy Manager es la única opción realmente accesible.