Descripción del proyecto
El homelab es un proyecto personal de infraestructura que arrancó con la idea de auto-gestionar todos los servicios que normalmente se delegan a terceros: contraseñas, DNS, monitorización, automatización... Hoy es un entorno de producción real que funciona 24/7.
El nodo principal (homelabES, Debian 12) concentra los servicios públicos y la gestión: Grafana, Prometheus, n8n, Vaultwarden, Nginx Proxy Manager, AdGuard Home, Homarr y Portainer. El nodo secundario (hlia) actúa de redundancia con Pi-hole, Netdata y Blocky.
Toda la comunicación entre nodos — y el acceso remoto seguro — se hace via Tailscale VPN mesh, sin exponer ningún puerto al exterior.
Arquitectura
- Dos servidores físicos conectados via Tailscale con IPs estables en la tailnet
- DNS de 4 niveles: AdGuard Home → Pi-hole → Blocky → Cloudflare/1.1.1.1
- Todos los servicios en contenedores Docker gestionados con Portainer + Watchtower
- Backups bidireccionales entre nodos con Syncthing
- Acceso HTTPS a todos los servicios internos via Nginx Proxy Manager con SSL
- Panel central con Homarr para acceder a todos los servicios desde una sola URL
Stack de servicios
docker-compose.yml completo del nodo principal (homelabES) — 11 servicios, ~4 GB RAM:
# homelabES — Nodo Principal
services:
nginx-proxy-manager:
image: jc21/nginx-proxy-manager:latest
ports:
- "80:80"
- "443:443"
- "81:81"
volumes:
- npm_data:/data
- npm_letsencrypt:/etc/letsencrypt
restart: unless-stopped
adguardhome:
image: adguard/adguardhome:latest
ports:
- "53:53/tcp"
- "53:53/udp"
- "3002:3000"
volumes:
- adguard_work:/opt/adguardhome/work
- adguard_conf:/opt/adguardhome/conf
restart: unless-stopped
grafana:
image: grafana/grafana:latest
user: "472"
ports:
- "3000:3000"
volumes:
- grafana_data:/var/lib/grafana
restart: unless-stopped
prometheus:
image: prom/prometheus:latest
ports:
- "9090:9090"
volumes:
- prometheus_data:/prometheus
- ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
restart: unless-stopped
node-exporter:
image: prom/node-exporter:latest
ports:
- "9100:9100"
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/rootfs:ro
command:
- '--path.procfs=/host/proc'
- '--path.sysfs=/host/sys'
restart: unless-stopped
uptime-kuma:
image: louislam/uptime-kuma:latest
ports:
- "3001:3001"
volumes:
- uptime_data:/app/data
restart: unless-stopped
vaultwarden:
image: vaultwarden/server:latest
ports:
- "8080:80"
volumes:
- vw_data:/data
environment:
- WEBSOCKET_ENABLED=true
restart: unless-stopped
ntfy:
image: binwiederhier/ntfy:latest
ports:
- "2586:80"
volumes:
- ntfy_cache:/var/cache/ntfy
- ntfy_etc:/etc/ntfy
command: serve
restart: unless-stopped
n8n:
image: n8nio/n8n:latest
ports:
- "5678:5678"
volumes:
- n8n_data:/home/node/.n8n
environment:
- N8N_BASIC_AUTH_ACTIVE=true
restart: unless-stopped
syncthing:
image: syncthing/syncthing:latest
ports:
- "8384:8384"
- "22000:22000/tcp"
- "22000:22000/udp"
volumes:
- syncthing_data:/var/syncthing
restart: unless-stopped
watchtower:
image: containrrr/watchtower:latest
volumes:
- /var/run/docker.sock:/var/run/docker.sock
command: --interval 86400
restart: unless-stopped
homarr:
image: ghcr.io/ajnart/homarr:latest
ports:
- "6575:7575"
volumes:
- homarr_configs:/app/data/configs
- homarr_icons:/app/public/icons
- /var/run/docker.sock:/var/run/docker.sock
restart: unless-stopped
docker-compose.yml del nodo secundario (hlia) — 6 servicios, ~2 GB RAM:
# hlia — Nodo Secundario (Redundancia) services: pihole: image: pihole/pihole:latest ports: - "53:53/tcp" - "53:53/udp" - "8090:80" volumes: - pihole_etc:/etc/pihole - pihole_dnsmasq:/etc/dnsmasq.d environment: - TZ=Europe/Madrid - WEBPASSWORD=restart: unless-stopped blocky: image: spx01/blocky:latest ports: - "5353:53/udp" - "5353:53/tcp" volumes: - ./blocky-config.yml:/app/config.yml:ro restart: unless-stopped netdata: image: netdata/netdata:latest ports: - "19999:19999" cap_add: - SYS_PTRACE security_opt: - apparmor:unconfined volumes: - netdata_config:/etc/netdata - netdata_lib:/var/lib/netdata - /proc:/host/proc:ro - /sys:/host/sys:ro - /var/run/docker.sock:/var/run/docker.sock:ro restart: unless-stopped node-exporter: image: prom/node-exporter:latest ports: - "9100:9100" volumes: - /proc:/host/proc:ro - /sys:/host/sys:ro - /:/rootfs:ro command: - '--path.procfs=/host/proc' - '--path.sysfs=/host/sys' restart: unless-stopped syncthing: image: syncthing/syncthing:latest ports: - "8384:8384" - "22000:22000/tcp" - "22000:22000/udp" volumes: - syncthing_data:/var/syncthing restart: unless-stopped tailscale: image: tailscale/tailscale:latest network_mode: host cap_add: - NET_ADMIN - NET_RAW volumes: - tailscale_data:/var/lib/tailscale - /dev/net/tun:/dev/net/tun environment: - TS_AUTHKEY= - TS_STATE_DIR=/var/lib/tailscale restart: unless-stopped
Capturas de pantalla
Añade aquí capturas de tus dashboards, paneles y configuraciones.
Dashboard Grafana
AdGuard Home
Workflows n8n
Uptime Kuma
Retos y soluciones
- DNS móvil desde fuera de casa: resuelto configurando AdGuard Home como nameserver global de Tailscale, con Cloudflare como fallback.
- Grafana persistent volume: problema con permisos de directorio en Docker; solución con
user: "472"en compose. - n8n y cabeceras emoji: violación RFC en headers HTTP; resuelto sanitizando los nombres de workflow.
- Pi-hole v6 password reset: sintaxis distinta en v6;
pihole setpasswordreemplaza el método anterior.
Aprendizajes
Este proyecto me ha dado una comprensión práctica y profunda de Docker, networking, DNS, reverse proxies y gestión de servicios en producción real. Cada problema encontrado ha sido una oportunidad de aprender cómo funcionan realmente las capas de infraestructura.
Lo más valioso: la capacidad de diagnosticar y resolver problemas de red, contenedores y configuraciones bajo presión — una habilidad directamente aplicable en entornos profesionales IT.