Skip to content

Homelab Architecture — Reference

Last updated: 2026-08-01

Documento canónico de la arquitectura del homelab. Aquí va lo que está vivo, no lo que está planeado. Para historia/decisiones puntuales ver el git log de este repo.

🖥 Hardware

Cluster Proxmox mini-pcs — 2 nodos físicos + QDevice (quórum):

Nodo IP Hardware RAM
proxmox 192.168.0.50 AMD Ryzen 7 7735HS (8c/16t, Zen3+) + Radeon 680M iGPU 28 GB
proxmox2 192.168.0.51 Intel Celeron N5095 (4c/4t, sin AVX2) 8 GB

NVMe: Kingston OM8PGP41024Q-A0 1TB en proxmox-50.

⚠️ N5095 sin AVX2: cualquier binario que requiera AVX2 (Bun, ciertos build de Node, modelos LLM nativos) falla con SIGILL. Usar variantes "baseline" siempre.

🏗 Workloads

VMs (en proxmox)

ID Nombre IP Rol
171 homeassistant 192.168.0.171 HAOS — Home Assistant 17.2.rc2
208 ubuntu-media-server 192.168.0.208 Docker host (~90 containers, ~76 running — verificado 2026-08-01)

LXCs (en proxmox, storage zfs-ha)

ID Nombre IP Rol
100 clawdbot (OpenClaw) 192.168.0.203 AI assistant (Telegram bot @Veraclawd_bot). VMID migró 253→100
110 tv-gw 192.168.0.210 L3 gateway monitoring LG TV
172 ha-ml 192.168.0.201 Modelo ML arrival_predictor (HA)
186 pbs 192.168.0.22 Proxmox Backup Server (datastore destino) — vive en proxmox/pmx-50
251 rag 192.168.0.202 RAG offline-kit + Kiwix ZIM
270 caddy-primary 192.168.0.40 Caddy HA MASTER (VIP keepalived .250)
280 rp-server 192.168.0.205 Reverse-proxy / server host
281 wp-pulse-host 192.168.0.211 Host WordPress Pulse

CT 102 iarq-backup-target, LXC 101 (hermesbot), LXC 249 (beszel) y LXC 250 (kiwix) están DESTRUIDOS (confirmado 2026-08-01 vía pvesh get /cluster/resources --type vm — no aparecen). Quitados de este inventario.

LXCs (en proxmox2, storage zfs-ha)

ID Nombre IP Rol
123 cloudflared 192.168.0.207 Cloudflare Tunnel
155 wireguard 192.168.0.206 VPN
200 n8n 192.168.0.200 Automatizaciones + alert forwarder centralizado
271 caddy-secondary 192.168.0.41 Caddy HA BACKUP (VIP keepalived .250)

Otros nodos (no Proxmox)

Host IP Rol
pi-hole (Raspi) 192.168.0.204 DNS adblock (Pi-hole 6, admin :8080)
Terramaster NAS 192.168.0.20 NFS (/Volume1/backups, /Volume1/Media)
bazzite.lan (GPU box) 192.168.0.141 RTX 3080 10GB, Bazzite OS (Linux). SillyTavern/koboldcpp para rol.monxas.casa, wake-on-demand + MQTT/HA (modo juego, apagado automático). Encendido manual/WoL (MAC d4:5d:64:ed:bb:d1)

📦 Stack Docker (VM 208)

/home/monxas/stacks/ agrupa los compose por área:

Stack Compose Highlights
infra/ observabilidad + reverse proxy Prometheus, Grafana, Loki, Promtail, blackbox-exporter, healthchecks, pihole-exporter, ntfy, dockge, dozzle, cAdvisor, node-exporter, Alertmanager, Pushgateway, homepage, uptime-kuma, unpoller
media/ media stack Jellyfin, Sonarr, Radarr, Lidarr, Prowlarr, Bazarr, Plundrio (3 containers), audiobookshelf, tunarr, metube, pinchflat, twitch-rec, FlareSolverr, jellystat, jellyfin-share
music/ (no había fila propia antes) audio stack Navidrome, Music Assistant, slskd, soularr, sonic-galaxy, shairport-sync, beets, music-reorg-ui
tools/ utilities Paperless-ngx, Immich, Karakeep, Stash, Stirling-PDF, code-server, pairdrop, ChangeDetection, Pocket-ID, ntfy, isponsorblocktv
reverse-proxy/ (legacy) ⚠️ El container Caddy fue eliminado de la VM 208 (post-F4.f). El routing vive ahora en LXC 270 caddy-primary + LXC 271 caddy-secondary tras VIP keepalived .250. Ver services-routing.md
projects/ apps custom finanzas, okrs, api-proxy, TRMNL, trip-planner, jira-tickets, justeat-tracker, justeat-menu-api, arquitectos, docs-home… (OAP vive en CT100, no en 208)

Verificado 2026-08-01 vía docker ps en VM 208 (~90 containers totales, ~76 running en el momento del chequeo — el resto duermen vía Sablier, ver services-routing.md). Eliminados/decomisionados (per services-routing.md, 2026-07-26): krawl, speedtest-tracker, paddleocr/ocr, markdown-tool (md/md-demo/md-api), piso, web-monitor. Esta tabla es "highlights", no exhaustiva — hay más contenedores nuevos que no estaban documentados aquí (drift acumulado, esta pasada solo corrige lo obviamente roto/borrado).

Caddy expone admin en :2019 con metrics directive habilitada (necesario para scrape Prometheus).

📊 Observabilidad

Logs — Loki

http://192.168.0.208:3101 (puerto host es 3101, internal docker es 3100)

Hosts en Loki (9): - proxmox-50, proxmox2-51 (vía promtail systemd /usr/local/bin/promtail v3.6.10) - ubuntu-media-server (vía promtail container, scrape Docker socket + /var/log) - lxc-100, lxc-123, lxc-155, lxc-186, lxc-200 (el nodo Proxmox correspondiente lee el journal de cada LXC desde /zfs-ha/subvol-<ID>-disk-0/var/log/journal) - ha-171 (vía systemd service ha-log-pusher en proxmox-50 — websocket subscribe a system_log_event, automation_triggered, script_started, etc., push a Loki)

Retention: 30 días (limits_config.retention_period: 720h).

Desde 2026-04-25 los logs Docker de VM 208 llevan también label host=ubuntu-media-server (antes solo container_name). Permite query unificada por host. Detalles + queries: ../operations/logs.md.

Métricas — Prometheus

http://192.168.0.208:9090 (host network)

Retention: 30d / 5GB. Targets activos (38 en 23 jobs, verificado 2026-08-01 vía /api/v1/targets): | Job | Targets | Qué | |-----|---------|-----| | node | 1 | localhost VM 208 | | proxmox-nodes | 2 | pmx-50.lan, pmx-51.lan | | cadvisor | 1 | containers VM 208 | | caddy | 2 | caddy1.lan:2021, caddy2.lan:2021 (métricas, no el admin :2019) | | pihole | 1 | pihole-exporter (localhost:9617 en VM 208) | | raspi-pihole | 1 | node-exporter en el propio host Pi-hole (pihole.lan:9100) | | homeassistant | 1 | HA ha.lan:8123 | | ha-ml, ha-ml-node | 1 c/u | app :8000 + node-exporter :9100 en ha-ml.lan | | n8n-node | 1 | node-exporter n8n.lan:9100 | | tv-gw | 1 | node-exporter tv-gw.lan:9100 | | tnas-f4423 | 1 | node-exporter NAS nas.lan:9100 | | unifi, unifi-clients | 1 c/u | exporter UniFi (Integration API) | | asus-router | 1 | exporter del router ASUS | | remote-pulse-server | 1 | lxc280 (RP fleet) | | openclaw-vm-node, openclaw-vm-process | 1 c/u | Tailscale 100.107.67.103 (Mac mini Carmelo, openclaw-vm) | | blackbox-http-internal | 7 | Grafana, Prom, Loki /ready, cadvisor, Jellyfin LAN, HA, n8n /healthz | | blackbox-https-external | 6 | jellyfin/sonarr/radarr/pics/ntfy/wp-pulse .monxas.casa | | blackbox-icmp | 3 | router (.1), 8.8.8.8, 1.1.1.1 | | pushgateway, prometheus | 1 c/u | |

Jobs nuevos desde la última pasada de docs: asus-router, ha-ml-node, n8n-node, openclaw-vm-node/-process, raspi-pihole, remote-pulse-server, unifi/unifi-clients. El puerto de caddy cambió de :2019 (admin) a :2021 (métricas dedicadas) en algún momento no documentado.

Dashboards Grafana

http://192.168.0.208:3030 — login admin / <see secrets.local.md>

Reorganizados 2026-08-01: auditoría + limpieza + fusión de dashboards solapados, 24→15 dashboards en 7 carpetas, nuevo dashboard super-panel-salud como entrada principal. Lista completa verificada vía API ese mismo día:

Dashboard UID Carpeta/Emoji
Overview — Salud del Homelab super-panel-salud 🩺 Overview (nuevo, entry point)
Red — Router + UniFi (resumen) red-homelab 🌐 Red
Red — tv-gw (todo) red-tv-gw-todo 🌐 Red
Red — UniFi (detalle) red-unifi-detalle 🌐 Red
Red — UniFi Client Insights jMfvAjxWz 🌐 Red
Servicios — Blackbox (probes HTTP/ICMP) xtkCtBkiz 🐾 Servicios
Servicios — Caddy (completo) servicios-caddy-completo 🐾 Servicios
Servicios — Pi-hole pihole-stats 🐾 Servicios
Servicios — Remote-Pulse Fleet remote-pulse-fleet 🐾 Servicios
NAS — HyperCache (Crucial P310) nas-hypercache 💾 NAS
Logs — Overview (todos los hosts) homelab-overview 📜 Logs
Infra — Home Assistant ha-complete-001 🖥️ Infra
Infra — Máquinas físicas homelab-fisicos 🖥️ Infra
Infra — Node Exporter (hosts) rYdddlPWk 🖥️ Infra
iarq — Stack (Carmelo VM) iarq-stack 🖧 iarq

Dashboards viejos que ya NO existen (fusionados/retirados en el reorg): "Docker monitoring" (771d9eba-...), "Logs / App" (sadlil-loki-apps-dashboard, fusionado en homelab-overview), "Speedtest Tracker Dashboard" (el servicio se decomisionó). Tras importar dashboards comunitarios siempre arreglar: (a) datasource: None → UID real (ef6zpdikyp0cgf Prom, bf6zpf6y2cxs0e Loki); (b) defaults de template vars; (c) time range razonable.

Alertas — centralizadas via Alertmanager

Ver alerting-flow.md para el detalle completo (política "silencio primero": todo va callado a Vera por defecto, solo un carril reducido de alertas graves interrumpe por Telegram al instante). Resumen (recuento verificado 2026-08-01 vía API, coincide con alerting-flow.md): 20 reglas Grafana (/api/v1/provisioning/alert-rules) + 52 reglas Prometheus (alerts.yml en 7 grupos, incl. CriticalDiskUsage >90%) + 9 checks Healthchecks + OpenClaw watchdog + Morning report → convergen en Alertmanager (central, receptores telegram-grave+vera) + webhook PVE→Telegram + n8n (legacy) → Telegram @Veraclawd_bot.

💾 Backups

Origen Destino Frecuencia Retención
vzdump VMs (208, 171) PBS CT 186 (pmx-50) diario 03:00 keep-last=7, keep-weekly=4, keep-monthly=3
vzdump LXCs (123, 155) PBS CT 186 (pmx-50) diario 01:00 idem
vzdump LXC 200 (n8n) PBS CT 186 (pmx-50) diario 02:00 idem
vzdump LXC 281 (wp-pulse) PBS CT 186 (pmx-50) diario 02:00 idem
vzdump LXC 100 (clawdbot) PBS CT 186 (pmx-50) diario 04:30 idem
vzdump LXCs (270, 271, 280) PBS CT 186 (pmx-50) diario 05:00 idem
vzdump LXCs (110, 172, 251) PBS CT 186 (pmx-50) diario 05:30 idem
PBS verify (datastore main) self sábados 02:00 outdated-after=7d
PBS verify (datastore archive) self domingos 03:00 outdated-after=14d
PBS GC (garbage collect) both datastores diario
PBS restore test LXC 999 temporal en zfs-ha día 5 cada mes 04:30 auto-destroy
Mirror docs git NAS Terramaster /mnt/pve/terramaster/infra-docs/ diario 03:30 git mirror, histórico

CT 102 (iarq-backup-target) fue destruido — quitado de la fila de vzdump. El restore test (/usr/local/bin/pbs-restore-test.sh en proxmox2-51) restaura el último backup del LXC 123 (más pequeño) a un VMID temporal 999, verifica config, lo destruye. Si falla → alerta.

Datastore main al 41% usado, archive al 54% (verificado 2026-08-01, df -h en LXC 186). Ya no está al ~82% — ver memoria pbs_main_space_20260731.md: se recortó keep-daily 3→2 y el GC liberó espacio en un ciclo posterior.

🛠 Crons centralizados (con ping a Healthchecks)

Cron Host Schedule Healthchecks
morning-report.py proxmox-50 08:00 diario ping 6cf57bf7-...
mirror-homelab-repo.sh proxmox-50 03:30 diario ping 382963b9-...
cron.weekly/homelab-cleanup proxmox-50 sáb 03:30 ping 671596df-...
cron.weekly/homelab-cleanup proxmox2-51 sáb 03:30 ping e6ef7611-...
cron.weekly/homelab-cleanup VM 208 sáb 03:30 ping 66131d54-...
pbs-restore-test.sh proxmox2-51 día 5 04:30 ping 5ebb4d84-...
smart-collect.sh proxmox-50 hourly ping 356de4be-...
smart-collect.sh proxmox2-51 hourly ping 6b1ce80a-...
duck.sh (DuckDNS) proxmox-50 (root) cada 15min ping e4058286-...

Healthchecks self-hosted en http://192.168.0.208:8073 (login [email protected] / <see secrets.local.md>). Si un cron NO hace ping en su grace window → alerta a Telegram via webhook integration.

🔐 Reverse proxy + TLS

Caddy vive ahora en LXCs propios (post-F4.f), ya NO en la VM 208 (el container Caddy fue eliminado): - LXC 270 caddy-primary (.40, movido de .247, en proxmox/pmx-50) — VIP MASTER, source-of-truth del routing. - LXC 271 caddy-secondary (.41, movido de .248, en proxmox2/pmx-51) — VIP BACKUP. - Ambos tras el VIP keepalived .250 (VRRP). Build custom con plugins caddy-dns/cloudflare + sablierapp/sablier. - TLS wildcard *.monxas.casa via Cloudflare DNS-01 challenge, auto-renewal ACME ARI. Admin :2019 con metrics. - Único ingress: Cloudflare → cloudflared → VIP .250:443 → Caddy LXC 270/271 → reverse_proxy 192.168.0.208:<host_port>.

Cloudflare Tunnel en LXC 123 (cloudflared, .207, token-based via dashboard) — ingress unificados apuntando al VIP .250:443 con noTLSVerify=true + originServerName=<hostname>. Caddy hace SNI matching sobre el wildcard cert y rutea por hostname.

Sablier — middleware en Caddy que para containers idle en VM 208 y los arranca on-demand cuando llega request al hostname. 17 hostnames lazy (verificado 2026-08-01, contando bloques sablier{} en managed.caddy de LXC 270). Lista completa y cómo añadir/dormir nuevos servicios → services-routing.md.

Routing CLI: homelab-ctl.py (reemplazó tunnel-sync.py + sablier-watchdog.py; subcomandos status/sync/watch/update/smoke/remove) reconcilia labels tunnel.* + rutas estáticas de ~/scripts/tunnel-static.yaml. Ver services-routing.md.

SSO via Pocket-ID (container en VM 208, :1411, 11 clientes OIDC) — para servicios protegidos por Cloudflare Access.

🤖 OpenClaw (CT 100 clawdbot)

Ver openclaw-setup.md para detalles. Resumen (verificado 2026-08-01 en vivo, CT 100): - OpenClaw 2026.7.1 (npm ls -g openclaw; doc anterior decía 2026.4.23) - Modelo primary: anthropic/claude-sonnet-5 (vía claude-cli runtime, OAuth Claude Code subscription) — openai/gpt-5.5 es solo el último fallback en la cadena (anthropic/claude-opus-5claude-sonnet-4-6claude-opus-4-8gpt-5.5), NO el primary. El doc anterior tenía esto invertido (ver memoria vera_anthropic_authprofile_20260725.md: gpt-5.5 como primary era el síntoma de un perfil auth roto, no el estado normal). - Bot Telegram: @Veraclawd_bot (token compartido con n8n forwarder y Grafana alerts) - Gateway en 127.0.0.1:9943 (UI via nginx :9944), browser control :9945 - OAP API+frontend (port 3000/3001) — subapp de analytics - Watchdog ACTIVO (openclaw-watchdog.timer enabled+active, verificado 2026-08-01 — contradice el estado "desactivado" que decía este doc antes; no hay evidencia de cuándo se reactivó)

🧠 GPU box — bazzite.lan (192.168.0.141)

Caja dedicada con GPU fuera del cluster Proxmox, no gestionada por Proxmox. RTX 3080 10GB corriendo Bazzite OS (distro Linux gaming). Rol actual: SillyTavern + koboldcpp para rol.monxas.casa, con integración MQTT+HA (modo juego libera VRAM, apagado automático nocturno, wake-on-demand — entrar a rol.monxas.casa con la máquina apagada la enciende sola vía el waker en 208:8477 + failover de Caddy). Encendido manual/WoL (MAC d4:5d:64:ed:bb:d1).

Esta sección describía antes "Casa Brain" (Windows + WSL2, Ollama :11434 + imagegen SDXL-Turbo :9002) — ese rol no sigue vivo aquí; la máquina pasó a Linux/Bazzite por completo. La caja Windows+WSL2 con RTX 3080 que sí sigue activa para AI Tagger es otra máquina, en .127 (DHCP, "sobremesa").

🔑 Accesos

Credenciales reales en secrets.local.md (gitignored). Mapa de cuentas:

Destino Usuario Variable secrets.local.md
proxmox-50, proxmox2-51 root PROXMOX_ROOT_PASS
ubuntu-media-server monxas (ProxyJump via 50) MONXAS_SSH_PASS
HAOS add-on SSH hassio HASSIO_SSH_PASS
Grafana admin GRAFANA_ADMIN_PASS
Healthchecks [email protected] (compartido )
n8n web UI (ver UI)
NUT UPS monxas NUT_MONXAS_PASS
Pi-hole admin (no user) (compartido )
Telegram bot Veraclawd_bot TELEGRAM_BOT_TOKEN
OpenAI API OPENAI_API_KEY (sin saldo, fallback)
Anthropic OAuth ANTHROPIC_OAUTH_TOKEN (Claude Code subscription, sin saldo)

🧹 Cleanup automático

/etc/cron.weekly/homelab-cleanup en los 3 hosts (proxmox-50, proxmox2-51, VM 208): - Borra vzdump logs huérfanos (IDs no existentes) - find /var/log -name "*.gz|*.N" -mtime +30 -delete - journalctl --vacuum-time=14d - apt autoremove + autoclean - En VM 208: docker builder prune -af, docker image prune -f, docker volume prune -f

📚 Otros docs en este directorio

  • alerting-flow.md — flow centralizado de alertas (Grafana + HC + watchdog + morning → n8n → Telegram)
  • openclaw-setup.md — config OpenClaw (auth OAuth ChatGPT, modelo, watchdog, etc.)
  • loki-setup-status.md — setup Loki original (legacy, ver homelab-architecture.md para estado actual)
  • cloudflare-access-pocketid.md — SSO con Pocket-ID
  • log-rotation-config.md — rotación de logs Docker/systemd
  • security-monitoring.md — fail2ban
  • secrets.example.md / secrets.local.md — credenciales (template + reales)
  • scripts/ — docs de scripts (jellyfin-wake, tunnel-sync, cleanup-rejected)
  • services/systemd.md
  • security/ — firewall UFW
  • stacks/ — docs por stack (infra, media, tools)