Nginx Proxy Manager: reverse proxy con Docker paso a paso
Nginx Proxy Manager es la forma más directa de montar un reverse proxy en tu homelab sin editar un solo fichero de Nginx. Aquí va la guía desde el docker-compose hasta el primer certificado SSL, con lo que puede salir mal por el camino.
Si tienes Docker, ya casi lo tienes todo
Si llevas un tiempo montando servicios en casa —Jellyfin, Vaultwarden, Home Assistant, lo que sea— seguramente ya conoces bien ese momento incómodo: tienes que acordarte de qué puerto va con qué servicio, compartir una URL del tipo 192.168.1.50:8096 con alguien, o lidiar con el aviso de «conexión no segura» cada vez que abres algo en el navegador. No es un drama, pero cansa.
Un reverse proxy resuelve exactamente eso: pone una capa entre internet (o tu red local) y tus contenedores, de modo que cada servicio tiene su propio subdominio limpio y HTTPS sin que tengas que tocar ficheros de configuración de Nginx a mano. Si eso suena a mucho lío, Nginx Proxy Manager es la herramienta que hace esa parte por ti desde una interfaz web.
En este post montamos NPM desde cero con Docker Compose, configuramos el primer proxy host y dejamos un certificado SSL real funcionando. No necesitas experiencia previa con Nginx ni con certificados; sí necesitas tener Docker instalado y, si quieres HTTPS público, un dominio que puedas gestionar.
Por qué importa
Sin editar Nginx
Configuras hosts, redirecciones y cabeceras desde una UI web. Cero sintaxis de configuración manual.
SSL automático incluido
Let’s Encrypt emite y renueva certificados cada 90 días. NPM lo gestiona solo cuando quedan menos de 30.
Despliegue en un compose
Tres puertos (80, 443 y el panel en el 81) y arranca. La base de datos por defecto es SQLite, sin dependencias extra.
Logs por host
Accede a logs de acceso y error de cada servicio directamente desde la interfaz, sin entrar al contenedor.
Lo que necesitas antes de levantar nada
NPM corre como contenedor Docker, así que lo primero es tener Docker y Docker Compose instalados en tu máquina. Si ya tienes un homelab con Proxmox o cualquier servidor Linux, lo más cómodo es desplegar NPM en una VM ligera o un LXC con Docker.
Además de Docker, necesitas:
- Un dominio o subdominio que controles. Si solo usas nombres locales como
.home.arpao.local, Let’s Encrypt no puede emitir certificados — tendrás que usar self-signed o una CA privada. - Los puertos 80 y 443 disponibles en el host donde corre NPM. Si vas a emitir certificados con Let’s Encrypt vía HTTP challenge, esos puertos también tienen que llegar desde fuera — es decir, port forwarding en el router apuntando a tu servidor.
- Visibilidad de red entre NPM y los servicios a los que quieres hacer proxy. Esto es más importante de lo que parece y lo detallo más abajo.
Antes de tocar NPM, anoto en papel el hostname interno y el puerto de cada servicio que quiero exponer. Así la configuración de cada proxy host va mucho más rápida.
Desplegar NPM con Docker Compose
La imagen oficial es jc21/nginx-proxy-manager, disponible en Docker Hub. El docker-compose.yml mínimo documentado en el README oficial es suficiente para empezar:
services:
app:
image: 'jc21/nginx-proxy-manager:latest'
restart: unless-stopped
ports:
- '80:80'
- '81:81'
- '443:443'
volumes:
- ./data:/data
- ./letsencrypt:/etc/letsencrypt
El puerto 80 y 443 son para el tráfico de usuario final. El 81 es el panel de administración — no lo expongas al exterior sin protección adicional.
SQLite vs MariaDB: cuándo importa
Por defecto NPM usa SQLite, que almacena todo en el directorio ./data. Para un homelab con 5-20 servicios, SQLite va perfectamente. Si quieres más control sobre los backups de la base de datos o planeas escalar a muchos hosts, puedes configurar MariaDB o MySQL externo añadiendo las variables de entorno correspondientes — está documentado en el README oficial.
Yo empecé con SQLite y sigo con SQLite. Menos piezas móviles.
Redes Docker: el punto que más quebraderos de cabeza da
NPM necesita visibilidad de red hacia los contenedores a los que hace proxy. Si tus servicios corren en redes Docker distintas a la de NPM, no los va a ver por nombre de contenedor.
Hay dos formas de resolverlo:
- Red compartida: defines una red Docker externa (
docker network create proxy) y conectas tanto NPM como los demás servicios a esa red. En el proxy host usas el nombre del contenedor como hostname. - IP del host: usas la IP interna del servidor donde corre el servicio y el puerto expuesto. Funciona aunque no compartan red, pero es menos elegante si cambias IPs con frecuencia.
networks:
proxy:
external: true
services:
app:
image: 'jc21/nginx-proxy-manager:latest'
networks:
- proxy
ports:
- '80:80'
- '81:81'
- '443:443'
volumes:
- ./data:/data
- ./letsencrypt:/etc/letsencrypt
Si usas red compartida, crea la red antes de hacer docker compose up.
Primer acceso y configuración inicial
Una vez levantado el contenedor, abres el navegador en http://<IP-del-servidor>:81. Las credenciales por defecto son email admin@example.com y contraseña changeme. NPM te pide cambiarlas en cuanto entras — hazlo antes de configurar nada más.
La interfaz es directa: menú superior con Hosts, SSL Certificates, Access Lists y Users. La mayor parte del tiempo vas a estar en Hosts → Proxy Hosts.
Configurar tu primer proxy host
En Proxy Hosts → Add Proxy Host rellenas tres campos básicos:
- Domain Names: el dominio público desde el que quieres acceder al servicio. Por ejemplo,
grafana.tudominio.com. - Forward Hostname / IP: el nombre del contenedor (si comparten red Docker) o la IP interna del servidor donde corre el servicio.
- Forward Port: el puerto en el que escucha el servicio internamente. Para Grafana suele ser 3000.
Dos opciones que activo casi siempre:
- Block Common Exploits: añade reglas básicas de Nginx contra exploits comunes. No es un WAF completo, pero cuesta cero activarlo.
- Websockets Support: necesario para servicios como Home Assistant, Portainer o cualquier app que use websockets. Si no lo activas y el servicio los usa, verás errores de conexión intermitentes que son difíciles de diagnosticar.
Guarda y prueba: si el DNS ya apunta al servidor correcto y el servicio está levantado, debería cargar en HTTP. El HTTPS viene en el siguiente paso.
Basic Auth y listas de acceso
NPM permite añadir autenticación básica por proxy host desde la pestaña Advanced, o usar Access Lists para combinar IPs permitidas con usuario/contraseña. Es útil para proteger servicios que no tienen su propio sistema de login — un panel de monitorización interno, por ejemplo.
Las Access Lists son más potentes: puedes hacer que un servicio solo sea accesible desde tu red local aunque técnicamente esté detrás del proxy público.
Certificados SSL con Let’s Encrypt
En la pestaña SSL de cada proxy host puedes solicitar un certificado de Let’s Encrypt en dos o tres clics. NPM gestiona la renovación automáticamente cuando quedan menos de 30 días para la expiración — los certificados de Let’s Encrypt duran 90 días.
HTTP Challenge: el caso más sencillo
Si el dominio apunta a tu IP pública y los puertos 80 y 443 están abiertos desde fuera, el challenge HTTP funciona sin configuración adicional. Activa también Force SSL para redirigir todo el tráfico HTTP a HTTPS, y HTTP/2 Support si el servicio lo soporta.
Importante: exponer los puertos 80 y 443 significa que esos servicios son accesibles desde Internet. No es malo per se, pero hay que ser consciente — usa contraseñas fuertes y no expongas servicios que no necesiten estar en la red pública.
DNS Challenge: wildcards y dominios sin exposición pública
Si quieres un certificado wildcard (*.tudominio.com) o si tu servidor no es accesible desde Internet, necesitas el DNS challenge. NPM lo soporta, pero requiere configuración adicional: darle credenciales API de tu proveedor DNS (Cloudflare, OVH, Namecheap…) para que pueda crear los registros TXT que valida Let’s Encrypt.
No es complicado, pero tampoco es automático. Y si el proveedor DNS tarda en propagar los registros TXT, el challenge puede fallar la primera vez. En Cloudflare suele ir rápido; en otros proveedores he tenido que esperar varios minutos y reintentar.
Dominios internos: opciones si no tienes dominio público
Si solo usas nombres locales, Let’s Encrypt no puede verificar que controlas el dominio. Las opciones en este caso:
- Certificado self-signed: NPM lo genera desde la UI. El navegador mostrará aviso de seguridad, pero el tráfico va cifrado.
- CA privada propia: con herramientas como step-ca puedes montar tu propia autoridad certificadora, emitir certificados internos y añadir el certificado raíz a tus dispositivos. Más trabajo inicial, sin avisos después.
Lo que me ha salido mal (y cómo evitarlo)
Llevo un par de años usando NPM en mi homelab y hay algunos tropiezos que se repiten. Los apunto porque no siempre aparecen en la documentación oficial.
El contenedor destino no es visible desde NPM
Síntoma: el proxy host aparece como activo pero al acceder al dominio devuelve «502 Bad Gateway». Causa más habitual: NPM y el servicio no comparten red Docker, o el nombre del contenedor no resuelve desde la red de NPM.
Diagnóstico rápido: docker exec -it <nombre-contenedor-npm> ping <nombre-servicio>. Si no resuelve, o compartes red o usas la IP directamente.
La renovación automática falla en silencio
La renovación automática funciona bien en la mayoría de casos, pero si cambias la IP pública o cierras los puertos temporalmente justo cuando toca renovar, el proceso falla sin notificación visible por defecto. NPM no envía alertas cuando una renovación no sale bien.
Buena práctica: revisa periódicamente la sección SSL Certificates del panel y verifica las fechas de expiración. Si ves uno próximo a vencer que no se ha renovado solo, puedes forzar la renovación manualmente desde la UI.
Logs por host para diagnosticar
NPM muestra los logs de acceso y error por host desde la propia interfaz — icono de las tres líneas en cada proxy host. Es suficiente para diagnosticar la mayoría de problemas sin necesitar entrar al contenedor. Para algo más detallado, docker logs <nombre-contenedor-npm> da el log del proceso completo.
Javier — Maker, sysadmin y trastero serial
Preguntas frecuentes
Q: ¿Vale NPM si solo tengo dominios locales sin IP pública?
A: Depende de lo que necesites. NPM funciona perfectamente como reverse proxy interno sin dominio público, pero Let's Encrypt no puede emitir certificados para dominios .local o .home.arpa. En ese caso tendrás que usar certificados autofirmados o montar tu propia CA privada con herramientas como Step-CA.
Q: ¿Cómo renueva los certificados SSL sin que yo haga nada?
A: NPM comprueba periódicamente la caducidad de cada certificado y renueva automáticamente cuando quedan menos de 30 días de validez (los certificados de Let's Encrypt duran 90 días). Si usas DNS challenge para wildcards, necesitas configurar las credenciales de tu proveedor DNS; si ese paso falla, la renovación también falla.
Q: ¿Qué pasa si Traefik o Caddy hacen lo mismo?
A: Traefik y Caddy son alternativas reales y válidas: Traefik se integra mejor con entornos dinámicos (etiquetas Docker automáticas) y Caddy tiene una sintaxis de configuración muy limpia. NPM destaca cuando quieres gestionar todo desde una UI web sin editar ficheros de configuración ni aprender sintaxis específica.
Q: ¿Por qué necesito abrir los puertos 80 y 443 en el router?
A: Para que Let's Encrypt pueda verificar que controlas el dominio mediante HTTP challenge, tu servidor debe ser accesible desde internet en esos puertos. Si no quieres o no puedes abrir puertos, el DNS challenge es la alternativa, pero requiere acceso a la API de tu proveedor DNS y configuración adicional.
Q: ¿Cuántos servicios puedo poner detrás de NPM a la vez?
A: No hay un límite documentado por número de hosts proxy; en la práctica está limitado por los recursos del servidor donde corre NPM. La base de datos por defecto es SQLite, suficiente para un homelab con decenas de servicios. Si escalas a entornos con alta concurrencia o muchos hosts, puedes configurar MariaDB externo como backend.











Deja una respuesta