Homelab Architecture — Reference¶
Last updated: 2026-07-12
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 (~75 containers) |
LXCs (en proxmox, storage zfs-ha)¶
| ID | Nombre | IP | Rol |
|---|---|---|---|
| 100 | clawdbot (OpenClaw) | 192.168.0.245 | AI assistant (Telegram bot @Veraclawd_bot). VMID migró 253→100 |
| 102 | iarq-backup-target | 192.168.0.113 | Backup target (proyecto iarq) |
| 110 | tv-gw | 192.168.0.210 | L3 gateway monitoring LG TV |
| 172 | ha-ml | 192.168.0.172 | Modelo ML arrival_predictor (HA) |
| 186 | pbs | 192.168.0.186 | Proxmox Backup Server (datastore destino) — vive en proxmox/pmx-50 |
| 251 | rag | 192.168.0.188 | RAG offline-kit + Kiwix ZIM |
| 270 | caddy-primary | 192.168.0.247 | Caddy HA MASTER (VIP keepalived .250) |
| 280 | rp-server | 192.168.0.196 | Reverse-proxy / server host |
| 281 | wp-pulse-host | 192.168.0.211 | Host WordPress Pulse |
LXCs (en proxmox2, storage zfs-ha)¶
| ID | Nombre | IP | Rol |
|---|---|---|---|
| 123 | cloudflared | — | Cloudflare Tunnel |
| 155 | wireguard | — | VPN |
| 200 | n8n | 192.168.0.200 | Automatizaciones + alert forwarder centralizado |
| 271 | caddy-secondary | 192.168.0.248 | 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.237 | NFS (/Volume1/backups, /Volume1/Media) |
| Casa Brain (GPU box) | 192.168.0.141 | RTX 3080 10GB (WSL2) — Ollama :11434 (qwen2.5vl:7b, qwen2.5:7b-instruct) + imagegen SDXL-Turbo :9002. 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, krawl, speedtest-tracker |
media/ |
media stack | Jellyfin, Sonarr, Radarr, Prowlarr, Bazarr, Plundrio (3 containers), audiobookshelf, tunarr, metube, pinchflat, twitch-rec, FlareSolverr |
tools/ |
utilities | Paperless-ngx, Immich, Karakeep, Stash, Stirling-PDF, PaddleOCR, code-server, pairdrop, ChangeDetection, Pocket-ID, ntfy |
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, piso, okrs, api-proxy, TRMNL, trip-planner, markdown-tool, web-monitor, jira-tickets, justeat-tracker, arquitectos… (OAP vive en CT100, no en 208) |
Caddy expone admin en
:2019conmetricsdirective 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 solocontainer_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 (35 en 20 jobs):
| Job | Targets | Qué |
|-----|---------|-----|
| node | 1 | localhost VM 208 |
| proxmox-nodes | 2 | proxmox-50, proxmox2-51 |
| cadvisor | 1 | containers VM 208 |
| caddy | 2 | admin :2019 en .247 y .248 (caddy-ha) |
| pihole | 1 | pihole-exporter (.208:9617 → .204) |
| homeassistant | 1 | HA :8123 |
| blackbox-http-internal | 7 | Grafana, Prom, Loki, ntfy, 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 |
| speedtest, prometheus | 1 c/u | (plundrio-* NO es job Prometheus) |
Dashboards Grafana¶
http://192.168.0.208:3030 — login admin / <see secrets.local.md>
| Dashboard | UID | Origen |
|---|---|---|
| Node Exporter Full | rYdddlPWk |
grafana.com/1860 |
| Docker monitoring | 771d9eba-8983-41be-af1b-14711a85b38d |
comunidad |
| Home Assistant Complete | ha-complete-001 |
comunidad |
| Logs / App | sadlil-loki-apps-dashboard |
comunidad |
| Speedtest Tracker Dashboard | speedtest-tracker |
comunidad |
Tras importar dashboards comunitarios siempre arreglar: (a)
datasource: None→ UID real (ef6zpdikyp0cgfProm,bf6zpf6y2cxs0eLoki); (b) defaults de template vars; (c) time range razonable.
Alertas — centralizadas via Alertmanager¶
Ver alerting-flow.md para detalle. Resumen: 29 reglas Grafana (17 enumeradas en alerting-flow + 12 más: BTRFS/RAID/NVMe/PBS-verify/n8n/openclaw…) + ~40 reglas Prometheus (alerts.yml, incl. CriticalDiskUsage >90%) + 9 checks Healthchecks + OpenClaw watchdog + Morning report → convergen en Alertmanager (central, 2 receptores telegram+vera) + webhook PVE→Telegram + n8n (legacy) → Telegram @Veraclawd_bot. Ver alerting-flow.md.
💾 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 (102, 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 |
El restore test (
/usr/local/bin/pbs-restore-test.shen 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.
🛠 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 (.247, en proxmox/pmx-50) — VIP MASTER, source-of-truth del routing.
- LXC 271 caddy-secondary (.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, 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. 13 hostnames lazy (2026-07-12). 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:
- OpenClaw 2026.4.23 (npm openclaw@latest)
- Modelo primary: openai-codex/gpt-5.5 (vía OAuth ChatGPT subscription, no API key)
- 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 DESACTIVADO (interfería con maintenance — systemctl disable openclaw-watchdog.timer)
🧠 Casa Brain — GPU box (192.168.0.141)¶
Caja dedicada con GPU fuera del cluster Proxmox (Windows + WSL2, NVIDIA RTX 3080 10GB). No gestionada por Proxmox; encendido manual/WoL (MAC d4:5d:64:ed:bb:d1).
- Ollama en
:11434— modelosqwen2.5vl:7b(visión) yqwen2.5:7b-instruct. - imagegen en
:9002— SDXL-Turbo (generación de imágenes on-prem).
Consumida por servicios del homelab para inferencia LLM/visión y generación de imágenes local (sin API externa).
🔑 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, verhomelab-architecture.mdpara estado actual)cloudflare-access-pocketid.md— SSO con Pocket-IDlog-rotation-config.md— rotación de logs Docker/systemdsecurity-monitoring.md— fail2bansecrets.example.md/secrets.local.md— credenciales (template + reales)scripts/— docs de scripts (jellyfin-wake, tunnel-sync, cleanup-rejected)services/systemd.mdsecurity/— firewall UFWstacks/— docs por stack (infra, media, tools)