Proyectos IT
01 / 06 En producción

Homelab
Infrastructure

Stack completo de servicios self-hosted en dos servidores Debian, conectados via Tailscale VPN mesh, con monitorización en tiempo real, gestión DNS de 4 niveles y backups automáticos.

Docker Debian 12 Tailscale Nginx Proxy Manager AdGuard Home Pi-hole Portainer
2 Servidores físicos
12+ Servicios activos
99.8% Uptime promedio
4 Niveles DNS

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

Diagrama de arquitectura RebirthNode — Homelab Dual: homelabES + hlia + Windows 11 + Tailscale VPN mesh
  • 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.

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 setpassword reemplaza 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.