El problema: 12 servicios, cero coordinación
Cuando empecé a autoalojar servicios, cada uno corría de forma independiente. Un servidor para el proxy, otro proceso para la base de datos, otro para el monitoreo. Actualizaciones manuales, configuraciones dispersas en archivos que nadie documentaba, y cero visibilidad de qué estaba corriendo y en qué estado.
El resultado predecible: cada componente aislado era un vector de mantenimiento. Si algo fallaba a las 3 AM, el diagnóstico empezaba desde cero.
La arquitectura: capas de defensa
La solución no fue simplemente “meter todo en Docker”. Fue diseñar una arquitectura por capas donde cada nivel tiene una función específica:
Capa 1: Aislamiento con Docker Compose
Cada servicio corre en su propio contenedor con recursos limitados. Un archivo compose.yml define toda la infraestructura. Levantar, actualizar o restaurar cualquier servicio es un solo comando.
services:
traefik:
image: traefik:v3
restart: unless-stopped
ports:
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
Capa 2: Proxy inverso con Traefik
Traefik actúa como punto de entrada único. Todo el tráfico pasa por él, lo que permite:
- SSL automático via Let’s Encrypt
- Routing basado en dominios (cada servicio tiene su subdominio)
- Rate limiting y headers de seguridad centralizados
- Dashboard de monitoreo en tiempo real
Capa 3: Autenticación centralizada con Authentik
En vez de que cada servicio maneje sus propios usuarios, Authentik provee SSO (Single Sign-On). Un solo login para todo. Esto elimina credenciales dispersas y permite auditar accesos desde un punto central.
Capa 4: Firewall con nftables
nftables reemplaza a iptables como firewall del sistema. Las reglas son declarativas y fáciles de auditar. Solo los puertos 443 (HTTPS) y 22 (SSH) están abiertos al exterior.
Capa 5: Detección de intrusos con Fail2Ban
Fail2Ban monitorea logs de SSH, Traefik y Authentik. Después de 5 intentos fallidos, la IP se banea por 24 horas. Esto reduce ataques de fuerza bruta a prácticamente cero.
Los errores que hubieran costado horas
Algunos errores que descubrí durante la configuración y que hubieran costado downtime significativo en producción:
- Volúmenes sin backup: Los primeros meses no tenía respaldos automatizados de los volúmenes Docker. Una actualización que corrompe datos = pérdida total.
- Logs sin rotación: Sin logrotate, los logs crecen indefinidamente. Un disco lleno a las 3 AM no es divertido.
- Docker socket expuesto: Montar
/var/run/docker.socksin:ro(read-only) da acceso root al contenedor. Un error que la mayoría de tutoriales no menciona.
Resultado
Stack unificado con monitoreo, backups automatizados, y zero-downtime deploys. Más de 12 meses de operación continua sin intervención manual para mantener los servicios corriendo.
La clave no fue la complejidad del setup, sino la simplicidad: cada capa hace exactamente una cosa, y todas son auditables desde un solo punto.