Convertí mi homelab en producción: observabilidad + un agente que la lee
Métricas, logs, alertas y un agente de IA que consulta mi infra en vivo — sobre un mini-PC, con contenedores least-privilege y secrets cifrados. La receta, y las decisiones honestas que la mayoría no cuenta.
“Tengo Docker y unas VMs” no es operar sistemas. Lo que separa un homelab de un entorno de producción no es cuántos servicios corres — es si sabes qué está pasando adentro y reaccionas cuando algo se rompe.
Así que le metí a mi homelab la misma disciplina que se le mete a producción: observabilidad de verdad, alertas que llegan a un humano, secrets cifrados, contenedores con el mínimo privilegio, y un agente de IA que lee toda esa telemetría. Todo en un mini-PC bajo el escritorio.
Esta es la receta — y, más importante, los trade-offs que tomé, incluyendo uno que dejé sin resolver a propósito.
El plano
Dos subsistemas. Arriba, cómo entra un request. Abajo, cómo el sistema se observa a sí mismo.
Internet → Cloudflare Tunnel (+ Zero-Trust Access) → nginx → servicios
│
node-exporter ┐ │ sirve
cAdvisor ─────┼──► Prometheus ──► Alertmanager ──► Discord ▼
Promtail ─────────► Loki AIOps agent
▲ ▲ (read-only)
└───┴──── PromQL / LogQL ───────┘
- Prometheus scrapea métricas cada 15s (retención 15 días).
- node-exporter da métricas del host (CPU, RAM, disco, red).
- Loki + Promtail centralizan los logs de todos los contenedores (7 días).
- Alertmanager rutea alertas a Discord.
- Grafana para dashboards, detrás de Cloudflare Access (Zero-Trust).
- Y un agente de IA que consulta Prometheus y Loki para responder preguntas en lenguaje natural.
Números: 19 contenedores, 0 puertos expuestos al internet, 0 contenedores de aplicación privilegiados.
Observabilidad, no “un dashboard bonito”
El stack completo es un Compose aislado. Lo importante es que las métricas del host salen del node-exporter (la fuente correcta), no de un script con privilegios:
node-exporter:
image: prom/node-exporter:v1.8.2
command:
- '--path.procfs=/host/proc'
- '--path.rootfs=/host/root'
volumes:
- /proc:/host/proc:ro
- /:/host/root:ro,rslave
security_opt: [ "no-new-privileges:true" ]
Prometheus fija 4 reglas de alerta que importan: target caído, RAM > 90%, CPU > 90%, disco < 15%. Cuando una se sostiene, Alertmanager la manda a Discord. El truco para Discord: acepta payloads estilo Slack si le agregas /slack al webhook.
Seguridad primero, de verdad
Aquí es donde la mayoría de homelabs “impresionantes” hacen trampa. Yo tenía un servicio corriendo privileged: true con network_mode: host — acceso total al kernel, irónicamente el contenedor más peligroso. Lo bajé a lo mínimo:
security-backend:
cap_drop: [ ALL ]
cap_add: [ DAC_READ_SEARCH ] # solo para leer un log
security_opt: [ "no-new-privileges:true" ]
De todas las capabilities a una de solo-lectura. El costo: perdí la visibilidad de red/puertos del host desde ese contenedor — que ahora vive en node-exporter, donde debe estar.
Los ~40 secrets pasaron de un .env en texto plano a SOPS + age: cifrados en reposo, la llave privada puede vivir fuera de la caja, y el deploy desencripta al vuelo. Soy honesto sobre esto: en un solo host la llave y el ciphertext conviven, así que el beneficio runtime es modesto — la ganancia real es cifrado-en-reposo y disaster recovery. Prefiero decir eso a venderlo como algo que no es.
El agente que lee la infra
La parte divertida. Le agregué a mi agente (GPT-OSS 120B vía Groq) tres herramientas read-only:
query_prometheus— corre PromQL sobre las métricas del host.search_logs— busca en Loki los logs de todos los contenedores.get_active_alerts— lista lo que está firing.
Ahora le pregunto en español y contesta con datos reales:
yo: ¿cómo está el CPU del host? agente: El CPU del host está en 2% y la RAM en 30%.
yo: muéstrame errores de la última hora agente: 11 registros de error, la mayoría de conexión en el componente querier…
Es read-only por diseño (está expuesto al internet público) y tiene budget de tokens y de dólares por día. Un prompt no puede mutar infra, y no me va a vaciar la cuenta.
Un detalle real: el modelo en Groq rompía el function-calling cuando las descripciones de las tools eran largas. La solución no fue pelearme con el modelo — fue acortar los schemas y mover el cheatsheet de PromQL al system prompt. Descripciones cortas, llamadas confiables.
La decisión que dejé sin resolver (a propósito)
cAdvisor no registra métricas por-contenedor en este host. La causa: mi Docker usa el storage driver overlayfs (el nuevo, de containerd), que cAdvisor v0.49 no sabe mapear a nombres de contenedor. Probé privileged: true — no ayuda, porque no es un problema de permisos.
Podría haber metido privileged para “que se vea que funciona”. No lo hice: sería un downgrade de seguridad sin beneficio real. Lo documenté como limitación conocida y seguí. Las métricas del host y los logs cubren las preguntas que de verdad hago.
Esa es, para mí, la diferencia entre un lab y operar sistemas: nombrar el failure mode y elegir el trade-off en vez de taparlo.
Lo que demuestra
No es “tengo Docker y unas VMs”. Es: construí y opero una infraestructura self-hosted con observabilidad, automatización, seguridad least-privilege y un agente de IA capaz de analizar el estado de los sistemas — y puedo explicar cada decisión.
El case-study visual completo, con diagrama de arquitectura, está en lalu.dev/homelab.