Cómo actualizar a Nginx Proxy Manager 2.16 sin perder hosts ni certificados
Índice de contenidos
- Puntos clave
- Qué cambia en Nginx Proxy Manager 2.16
- Qué fallo de seguridad corrige la 2.16 y a quién afecta
- Cómo monté la prueba
- Cómo hacer la copia de seguridad antes de actualizar
- Cómo actualizar de 2.15 a 2.16 con Docker Compose
- Qué comprobar después de actualizar
- Cómo proteger solo una ruta con las listas de acceso por ruta
- Qué muestra el nuevo visor de logs
- Cambios de la 2.16 que pueden romper algo
- Cómo volver a 2.15 si algo falla
- Preguntas frecuentes
- ¿Me afecta el fallo de seguridad si no uso certificados por DNS?
- ¿Tengo que pasar por la 2.15 antes de llegar a la 2.16?
- ¿Nginx Proxy Manager o Traefik para un servidor nuevo?
- Conclusión
- Fuentes
Nginx Proxy Manager 2.16.0 se actualiza cambiando la etiqueta de la imagen y recreando el contenedor. No trae migraciones de base de datos y en mi prueba conservó hosts, certificados y listas de acceso. Copia antes data y letsencrypt: volver a 2.15 cambiando solo la imagen deja sin protección las rutas que protejas con la función nueva.
Actualizar Nginx Proxy Manager de 2.15 a 2.16 es cambiar la etiqueta de la imagen y recrear el contenedor, y en mi prueba no se perdió ni un host, ni un certificado, ni una lista de acceso. La versión 2.16.0, publicada el 24 de septiembre de 2026, añade un visor de logs en el panel y listas de acceso por ruta, y cierra una inyección de órdenes que afectaba a las versiones anteriores. La probé el 28 de septiembre de 2026 con estado real: dos hosts proxy, una lista de acceso, un certificado propio y otro emitido por DNS-01 contra una autoridad ACME local. Tienes también la versión en inglés de esta guía.
Puntos clave
- La 2.16.0 no añade migraciones de base de datos: tras recrear el contenedor, los hosts, las rutas, la lista de acceso y los dos certificados quedaron idénticos y respondieron igual.
- El fallo de seguridad es una inyección de órdenes en el arranque, al escribir las credenciales DNS de los certificados; quien lo reportó listó 34 versiones afectadas, de la 2.9.14 a la 2.14.0, y el código vulnerable sigue en la 2.15.1.
- La 2.16 ya no deja en disco el token de tu proveedor DNS, pero el fichero que dejó la 2.15 sigue ahí hasta la primera renovación o hasta que lo borres.
- Certbot pasa de 5.6.0 a 5.8.0 y el catálogo de plugins DNS de 86 a 88 entradas; el de Azure cambia de paquete.
- Volver a la 2.15 cambiando solo la imagen conserva los datos, pero la primera vez que guardas un host la 2.15 regenera su configuración sin las listas de acceso por ruta. Restaura la copia.
Qué cambia en Nginx Proxy Manager 2.16
La 2.16.0 trae dos funciones nuevas y un paquete de arreglos de seguridad. Según las notas de la versión 2.16.0[1], estos son los cambios que tocan una instalación en marcha:
- Visor de logs: una página Logs en el panel para leer el registro del backend, el de Let’s Encrypt y los de cada host sin entrar al contenedor.
- Listas de acceso por ruta: cada ruta personalizada de un host proxy puede tener su propia lista de acceso.
- Certbot actualizado: las notas avisan de que los plugins DNS pueden necesitar ajustes.
- Seguridad: el arreglo de la inyección de órdenes, códigos QR de doble factor generados en el propio servidor, tokens invalidados al cambiar la contraseña y credenciales DNS borradas del disco tras cada ejecución de Certbot.
- reuseport: la opción de Nginx que reparte conexiones entre procesos, que en la 2.16.0 solo se aplica a los streams TCP y UDP.
Las notas abren con este aviso: "Certbot has been updated. Exercise caution with this upgrade as your DNS plugins may not work as expected and require tweaks to dependencies." La guía de plugins de Certbot de NPM[2] añade otro riesgo: usar más de un proveedor DNS en la misma instancia puede provocar conflictos de dependencias de Python.
Dentro de la imagen también cambian las piezas de base. La 2.15.1 llevaba Certbot 5.6.0 y OpenResty 1.29.2.5; la 2.16.0 lleva Certbot 5.8.0 y OpenResty 1.31.1.1. La imagen para arm64 pesa 468 MB comprimida en Docker Hub, frente a 445 MB la de la 2.15.1.
Qué fallo de seguridad corrige la 2.16 y a quién afecta
La 2.16.0 corrige una inyección de órdenes en el código que prepara los plugins de Certbot al arrancar el backend. El pull request 5498[3], abierto el 19 de abril de 2026, explica el origen. El campo con las credenciales del proveedor DNS se metía en una orden de shell ejecutada con child_process.exec(), y el escapado de comillas se hacía en el orden equivocado. Las notas de la versión lo resumen en una línea y no citan ningún CVE ni aviso de seguridad.
Para explotarlo hace falta una cuenta con permiso para gestionar certificados, y la orden inyectada se ejecutaría en el siguiente reinicio del backend, según el informe como root dentro del contenedor. Por eso el riesgo real depende de quién tiene cuenta en tu panel. Si eres el único usuario, el riesgo es bajo; si repartes cuentas con permiso sobre certificados, actualiza cuanto antes.
Quien lo reportó listó como afectadas 34 versiones, de la 2.9.14 a la 2.14.0, que era la última en abril. El arreglo entró en la rama de desarrollo el 7 de junio de 2026, después de publicarse la 2.15.1. Lo comprobé en el código de cada etiqueta. El fichero setup.js de la 2.15.1[4] conserva la orden vulnerable, y el de la 2.16.0[5] ya no escribe el fichero de credenciales al arrancar.
Cómo monté la prueba
La prueba corrió en una VM Linux arm64 con Docker, usando las imágenes oficiales jc21/nginx-proxy-manager:2.15.1 y 2.16.0 de Docker Hub. Para emitir un certificado real sin dominio público usé Pebble[6] 2.10.1, la autoridad ACME de pruebas de Let’s Encrypt. A su lado, un BIND 9.20.29 acepta actualizaciones RFC 2136 firmadas con una clave TSIG. Este es el servicio de NPM en mi compose.yaml, sin la red:
services:
npm:
image: jc21/nginx-proxy-manager:${NPM_TAG:-2.15.1}
restart: unless-stopped
ports:
- "127.0.0.1:24880:80"
- "127.0.0.1:24881:81"
- "127.0.0.1:24843:443"
environment:
LE_SERVER: "https://pebble:14000/dir"
REQUESTS_CA_BUNDLE: "/pebble/pebble.minica.pem"
volumes:
- ./data:/data
- ./letsencrypt:/etc/letsencrypt
- ./pebble/pebble.minica.pem:/pebble/pebble.minica.pem:ro
La variable NPM_TAG fija la versión, así que actualizar y volver atrás es cambiar un valor. Las dos variables de entorno solo existen en el laboratorio: LE_SERVER apunta NPM a Pebble en lugar de a Let’s Encrypt y REQUESTS_CA_BUNDLE hace que Certbot confíe en su certificado. En producción no las pongas. Los dos volúmenes son los de la guía de instalación de Nginx Proxy Manager con Docker, y son todo el estado que hay que copiar.
Con la 2.15.1 en marcha creé, por la API del panel, este estado:
- Una lista de acceso
solo-labcon un usuario y dos reglas: permitir la red de Docker y denegar el resto - Un certificado propio autofirmado para
app2.lab.test - Un certificado comodín
*.lab.testemitido por DNS-01 con el plugin RFC 2136 - Un host
app1.lab.testcon ese certificado y una ruta/admin, y otroapp2.lab.testprotegido entero por la lista de acceso
Las credenciales del plugin RFC 2136 se pegan en el campo de credenciales del certificado. Son estas, con el secreto cambiado:
dns_rfc2136_server = 10.248.80.10
dns_rfc2136_port = 53
dns_rfc2136_name = npm-key
dns_rfc2136_secret = tu_secreto_tsig
dns_rfc2136_algorithm = HMAC-SHA512
Cómo hacer la copia de seguridad antes de actualizar
La copia son las carpetas data y letsencrypt, y conviene hacerla con el contenedor parado para que la base de datos SQLite no cambie a mitad. Los ficheros son de root, así que los empaqueto desde un contenedor Alpine en lugar de hacerlo con sudo. Desde la carpeta del compose.yaml:
docker compose stop npm
docker run --rm -v "$PWD":/npm -w /npm alpine:3.22 \
tar czf npm-2.15.1.tar.gz data letsencrypt
docker compose start npm
La primera orden detiene solo NPM, la segunda empaqueta las dos carpetas y la tercera lo arranca otra vez. Si usas MariaDB o PostgreSQL en lugar de SQLite, añade un volcado de esa base de datos.
Trata ese fichero como un secreto. En la 2.15.1, la carpeta letsencrypt/credentials guarda en texto plano las credenciales de cada certificado DNS-01; en mi copia estaba el secreto TSIG completo. Guárdala cifrada, por ejemplo con restic para copias de seguridad cifradas.
Cómo actualizar de 2.15 a 2.16 con Docker Compose
La actualización son dos órdenes. La documentación de actualización de NPM[7] se limita a docker compose pull y docker compose up -d, y promete que el proyecto "will automatically update any databases or other requirements". Yo fijo la versión en lugar de usar latest:
NPM_TAG=2.16.0 docker compose pull npm
NPM_TAG=2.16.0 docker compose up -d npm
curl -s http://127.0.0.1:24881/api/
La última orden consulta la API del panel, que devolvió "version":{"major":2,"minor":16,"revision":0}. En el registro del arranque aparecieron dos líneas que interesan: Current database version: none, porque no había migraciones que aplicar, e Installed rfc2136, porque NPM reinstala en cada arranque los plugins DNS que usan tus certificados. Si ese plugin falla al instalarse, es el momento de mirar el aviso de Certbot.
Cambiar la etiqueta recrea el contenedor aunque no toques nada más. El artículo sobre por qué Docker Compose 5.5 recrea tus contenedores explica qué otros cambios lo provocan.
Qué comprobar después de actualizar
Tras la actualización comparé el estado con el que había antes, y fue idéntico. Un script consultó por la API los hosts, las rutas, las listas de acceso y los certificados, y probó cada dominio con y sin credenciales. Las respuestas coincidieron una a una: 200 en app1, 401 sin usuario en app2, 200 con él y 301 de HTTP a HTTPS. Los dos certificados servidos tenían el mismo número de serie.
Quedaba un detalle que la actualización no arregla sola. El fichero credentials-2, con el secreto TSIG en texto plano, seguía en /etc/letsencrypt/credentials/ porque lo dejó la 2.15.1 y la 2.16 no lo borra al arrancar:
docker compose exec npm ls -l /etc/letsencrypt/credentials/
docker compose exec npm certbot --version
El fichero desaparece en la primera renovación, porque la 2.16 lo escribe desde la base de datos justo antes de ejecutar Certbot y lo borra al terminar. También puedes borrarlo a mano: lo hice y la renovación siguiente funcionó igual. Renové el certificado DNS-01 desde el menú del certificado, opción Renew, y Certbot 5.8.0 con el plugin RFC 2136 lo renovó sin cambiar nada.
La renovación manual tiene una dificultad que no es nueva de la 2.16. Tras renovar, Nginx siguió sirviendo el certificado anterior, porque la función de renovación no recarga Nginx; el código es el mismo en la 2.15.1. Ejecuta docker compose exec npm nginx -s reload y comprueba el número de serie servido con openssl s_client.
Cómo proteger solo una ruta con las listas de acceso por ruta
Las listas de acceso por ruta sirven para dejar público un servicio y proteger solo una parte, como un /admin. Antes de la 2.16 la lista de acceso se aplicaba al host entero; para proteger solo una ruta había que escribir directivas de Nginx a mano en la configuración avanzada de esa ruta. Ahora se elige en la ventana del host, pestaña Custom Locations, en el selector Access List de cada ruta:

El selector ofrece Inherit from Proxy Host, que hereda la lista del host, o cualquier lista que tengas creada. Elegí solo-lab para /admin en app1.lab.test, que no tiene lista propia. El resultado fue el esperado: /admin respondió 401 sin usuario y 200 con él, y la raíz del host siguió respondiendo 200 a cualquiera.
NPM traduce esa elección en directivas dentro del bloque location /admin del fichero que genera: auth_basic, las reglas allow y deny de la lista y satisfy all. En la API el cambio es un campo nuevo, access_list_id, dentro de cada ruta. La 2.15.1 lo rechaza con un 400 y el mensaje data/locations/0 must NOT have additional properties, y ese detalle importa al volver atrás.
Qué muestra el nuevo visor de logs
El visor de logs es la página Logs del menú, y solo la ven los administradores. Tiene un selector de fuente con System (el registro del backend), Let’s Encrypt y una entrada por cada host, con un conmutador entre registro de acceso y de errores. Encima del texto hay un filtro por nivel, un buscador, un selector de 100, 200, 500 o 1000 líneas y los botones Live, Refresh y Download.
En la práctica sustituye a docker compose logs para las consultas del día a día. En la fuente System vi la renovación completa, con la orden de Certbot y el Congratulations, all renewals succeeded. En el registro de acceso de app1.lab.test aparecieron los 401 de /admin y los 200 de las peticiones con usuario. El límite de 1000 líneas viene del propio backend, así que para revisar un día entero de tráfico sigues necesitando el fichero o un sistema de logs.
Cambios de la 2.16 que pueden romper algo
La actualización fue limpia en mi laboratorio, pero hay cinco cambios que conviene revisar antes de tocar producción:
- Plugins DNS de Certbot: con Certbot 5.8.0, un plugin fijado a una versión antigua puede no instalarse. Mira el registro del arranque y la línea
Installed <plugin>. - Azure DNS: el catálogo cambia
certbot-dns-azureporcertbot-dns-azure-modern. Si usas Azure, renueva un certificado a mano tras actualizar para confirmar que funciona. - Tokens de la API: los tokens emitidos antes de un cambio de contraseña dejan de valer. En la 2.15.1 un token antiguo siguió respondiendo 200 tras cambiar la contraseña; en la 2.16.0 respondió 401. Si un script usa un token de larga duración, pide uno nuevo tras cada cambio.
- Permisos denegados: las peticiones sin permiso devuelven 403 en lugar de 404, algo que notará cualquier script que comprobase el 404.
- Configuración fallida: si Nginx rechaza la configuración de un host, la 2.16 conserva el fichero como
.conf.errpara que puedas ver qué falló.
Hay además una variable nueva que no aparece en las notas: NPM_ADMIN_PORT, que cambia el puerto interno del panel (81 por defecto). La vi en el script de arranque 45-admin-port.sh de la 2.16.0; no la necesitas para actualizar.
Cómo volver a 2.15 si algo falla
La vuelta atrás segura es restaurar la copia y arrancar la 2.15.1. Cambiar solo la etiqueta de la imagen parece suficiente, porque no hay migraciones y la 2.15.1 arranca con los datos de la 2.16. Probé ese camino y me he topado con una dificultad. Tras volver a la 2.15.1 con la lista por ruta activa, /admin seguía respondiendo 401 porque el fichero de Nginx generado por la 2.16 seguía en disco. En cuanto guardé el host desde la 2.15.1, esta regeneró la configuración sin la lista de la ruta y /admin pasó a responder 200 a cualquiera, sin ningún aviso.
La misma vuelta atrás volvió a escribir el fichero credentials-2 en disco, porque la 2.15.1 lo recrea al arrancar. Por eso restauro la copia completa:
docker compose stop npm
docker run --rm -v "$PWD":/npm -w /npm alpine:3.22 sh -c \
'rm -rf data letsencrypt && tar xzf npm-2.15.1.tar.gz'
NPM_TAG=2.15.1 docker compose up -d npm
Tras restaurar, el mismo script de comprobación dio un estado idéntico al de antes de actualizar. Ten en cuenta que restaurar también devuelve los certificados renovados después de la copia a su versión anterior; mientras no hayan caducado, siguen valiendo. Si has creado listas por ruta en la 2.16 y no quieres restaurar, pásalas al host entero o a la configuración avanzada antes de volver.
Preguntas frecuentes
¿Me afecta el fallo de seguridad si no uso certificados por DNS?
El código vulnerable solo procesa certificados de Let’s Encrypt con desafío DNS que tengan credenciales guardadas. Aun así, cualquier usuario con permiso para gestionar certificados puede crear uno, así que actualiza si tu panel tiene más de un usuario con ese permiso. Si eres el único administrador, el riesgo es menor, pero la 2.16 también deja de guardar tus tokens DNS en disco.
¿Tengo que pasar por la 2.15 antes de llegar a la 2.16?
Mi prueba partió de la 2.15.1, así que no puedo confirmar el salto desde versiones más antiguas. Las notas de la 2.15.0 avisan de un cambio mayor, con la imagen base en Debian Trixie y Certbot actualizado. Si vienes de la 2.14 o anterior, haz la copia, sube a la 2.15.1, comprueba que tus plugins DNS se instalan y después pasa a la 2.16.0.
¿Nginx Proxy Manager o Traefik para un servidor nuevo?
NPM sigue siendo la opción si quieres gestionar hosts y certificados desde un panel web. Si prefieres declarar las rutas como etiquetas en tus contenedores, compáralo con Traefik con Docker Compose. Para filtrar tráfico malicioso delante de cualquiera de los dos, añade CrowdSec como WAF comunitario.
Conclusión
Actualizar a Nginx Proxy Manager 2.16 son dos órdenes y, en mi prueba, conservó todo el estado. Merece la pena por el arreglo de seguridad, que afecta también a la 2.15.1, y porque deja de guardar en disco los tokens de tu proveedor DNS. Haz la copia con el contenedor parado, guárdala cifrada, renueva un certificado DNS-01 tras actualizar y recarga Nginx. Si tienes que volver atrás, restaura la copia: cambiar solo la imagen deja sin protección las rutas que protegiste con la función nueva.
Fuentes: [1] Notas de Nginx Proxy Manager 2.16.0[1], [2] Notas de Nginx Proxy Manager 2.15.1[8], [3] Pull request 5498: arreglo de la inyección de órdenes[3], [4] backend/setup.js en la 2.16.0[5], [5] Catálogo de plugins DNS de la 2.16.0[9], [6] Documentación de NPM: actualización[7], [7] Documentación de NPM: plugins DNS de Certbot[2], [8] Etiquetas de jc21/nginx-proxy-manager en Docker Hub[10], [9] Pebble, la autoridad ACME de pruebas[6].
Fuentes
Código fuente
Accede a todo el código fuente de este artículo en GitHub.
Ver en GitHub