Un archivo de configuración tumbó el login de todo mi servidor


Homelab · Postmortem
Nginx Docker Authentik Sysadmin Postmortem Self-hosting

Después de migrar mi wiki de Outline a AFFiNE, tocaba eliminar Outline: respaldo final, apagar sus contenedores, y limpiar su configuración de nginx. Un solo comando:

rm /opt/outline/nginx/conf.d/outline.conf

Reinicié nginx y seguí con lo siguiente. Minutos después, una revisión de rutina mostró que auth.schoperena.com — donde corre Authentik, y de donde depende el login de todos mis servicios self-hosted — estaba sirviendo la aplicación de AFFiNE en su lugar.

El archivo outline.conf que eliminé no era solo de Outline. Contenía tres bloques de servidor de nginx sin relación entre sí: outline.schoperena.com, auth.schoperena.com y s3.schoperena.com, agrupados en el mismo archivo porque se crearon juntos en el docker-compose.yml original, cuando instalé Outline por primera vez y agregué Authentik y un bucket S3 al mismo tiempo. Al eliminar el archivo completo, nginx se quedó sin ningún bloque para auth.schoperena.com, y por las reglas de SNI cualquier solicitud a ese dominio terminó cayendo en el primer bloque listen 443 ssl disponible, que resultó ser el de AFFiNE. No hubo errores ni registros de alerta: solo la aplicación equivocada respondiendo con un 200 OK perfectamente válido.

El archivo mal organizado no fue casualidad, sino el síntoma de un problema más profundo. El stack compartido de nginx y Authentik nunca se separó de Outline, ni siquiera en el nombre: el contenedor se llamaba outline_nginx, la red de Docker outline_default, y el directorio /opt/outline. Todo seguía haciendo referencia a Outline mucho después de que Outline dejara de ser lo importante ahí, y ese nombre facilita justamente el error que cometí — asumir que algo con “outline” en el nombre solo afecta a Outline.

Corregí la ruta de auth en minutos, pero el problema de fondo seguía sin resolverse, así que decidí separar todo correctamente: outline_nginx pasó a llamarse nginx-proxy, con su propio directorio /opt/nginx-proxy/. Authentik quedó con los mismos contenedores y el mismo volumen de datos, pero en su propio docker-compose.yml en /opt/authentik/, sin compartir identidad con nada más. Y la red interna outline_default pasó a llamarse edge, migrando los ocho contenedores que dependían de ella.

La parte que requirió más cuidado fue esa migración de red: ocho contenedores en producción, sin downtime real permitido. La solución fue conectar cada contenedor a la red nueva sin desconectarlo todavía de la vieja, verificar que todo seguía funcionando, y recién entonces desconectarlo de la anterior — en ningún momento quedó algo sin ruta de red disponible:

docker network connect edge printstash-frontend --alias printstash-frontend --alias frontend
docker network disconnect outline_default printstash-frontend
docker exec nginx-proxy nginx -s reload

Repetí este proceso, verificando cada paso, hasta que los ocho contenedores quedaron solo en edge y pude eliminar outline_default sin dejar nada pendiente. El nombre de un contenedor no es solo estética: es información que alguien —yo mismo, con prisa— va a confiar sin revisar dos veces. Cuando el nombre describe el origen histórico de algo en lugar de su función actual, deja de ser confiable. Ahora cada parte de mi infraestructura se llama según lo que hace, no según lo que se instaló junto a ella el primer día.

© 2026 Sebastian Choperena Solano — Construido con Astrofy