Skip to content

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 :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 (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 (ef6zpdikyp0cgf Prom, bf6zpf6y2cxs0e Loki); (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.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.

🛠 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 — modelos qwen2.5vl:7b (visión) y qwen2.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, 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)