Portada de Guía de Home Lab
Tutorial Acceso Libre 04 May, 2026

Guía de Home Lab

De cero a tu propio servidor en casa

Contenido

🏠 1. ¿Qué es un home lab y por qué montar uno?

Un home lab es un servidor (o conjunto de equipos) que vive en tu casa y corre los servicios que normalmente le pagarías a SaaS de terceros: gestor de contraseñas, almacenamiento, automatizaciones, IA, gestor de proyectos, lo que sea.

No es un "servidor de gamer" ni un NAS de fotos. Es infraestructura propia que tú controlas: tus datos no pasan por la nube de nadie más, tus pagos mensuales se vuelven una compra única de hardware, y aprendes Linux, redes y Docker en el camino.

¿Para quién aplica?

Sí aplica si eres:

  • Dev junior o intermedio que quiere subir el techo técnico. Montar y mantener un home lab te enseña más de sistemas que un curso de $200.
  • Freelancer o consultor pagando $150–$400/mes en SaaS para administrar tu propio negocio. Ese gasto se amortiza en menos de un año.
  • Emprendedor con equipo pequeño (2–10 personas) donde las licencias por usuario empiezan a doler.

NO aplica si eres alguien que necesita uptime 99.99% para clientes externos sin tener tiempo ni interés en mantener servidores. Para eso paga la nube; tu costo de oportunidad es mayor.

Cuánto te ahorras al año (precios típicos 2026, USD)

SaaS Plan típico Costo/mes Costo/año Alternativa self-hosted
Notion Plus, 1 usuario $10 $120 AppFlowy
Google Workspace Business Standard $14 $168 Nextcloud
Slack Pro, 5 usuarios $44 $528 Mattermost
1Password Business, 5 usuarios $40 $480 Vaultwarden
ChatGPT Plus 1 usuario $20 $240 Open WebUI + Ollama
Zapier Starter $30 $360 n8n
Total mínimo   $158 $1,896  

Y eso son solo seis servicios. La cuenta real, con todo el stack que vamos a montar, llega arriba de $3,000/año.


🔩 2. Hardware recomendado por tier

Tres tiers según presupuesto. No vas a necesitar el tier 3 para empezar; la mayoría se queda cómoda en el tier 2.

Tier Starter — alrededor de $200

Para aprender, correr 4–6 contenedores ligeros, y no asustarte si se quema.

Componente Modelo de referencia Rango USD Para qué sirve
Single Board Computer Raspberry Pi 5, 8GB RAM $80 El servidor en sí
Almacenamiento microSD 128GB clase A2, marca seria (SanDisk, Samsung) $20 Sistema operativo y datos
Fuente Oficial Raspberry Pi 27W USB-C $12 No te ahorres acá: alimentación inestable = corrupción de SD
Caja con disipador Argon ONE V3 o similar con ventilador $30 Disipación térmica seria
Cable Ethernet Cat6 2m $5 Wi-Fi para servidor es un pecado

Para qué alcanza: Vaultwarden, Homepage, n8n con uso liviano, Cloudflare Tunnel, Beszel. No corras Nextcloud serio ni Ollama acá.

Tier Intermedio — alrededor de $600

El sweet spot. Un mini PC con procesador Intel N100 o N150 te da 10× la potencia de la Pi por 3× el precio.

Componente Modelo de referencia Rango USD Para qué sirve
Mini PC Beelink EQ13 / MinisForum UN100 / GMKtec G3 (N100, 16GB RAM, 500GB NVMe) $280–$380 Servidor principal
UPS APC Back-UPS BE600M1 o CyberPower CP685AVR $80 Cortes de luz no te corrompan datos
Switch gigabit TP-Link TL-SG108 (8 puertos) $25 Para conectar más equipos sin pelearte con el router
Disco externo USB 3 WD Elements 2TB $70 Backups locales
Cable Ethernet Cat6 ×3 2m $15 El cableado es la infraestructura invisible

Para qué alcanza: Todo el stack de la sección 3 menos Ollama con modelos grandes. Soporta cómodamente 5–10 usuarios concurrentes.

Tier Pro — desde $1,500

Si esto es tu negocio o tu base de operaciones, vale la pena armarlo bien desde el inicio.

Componente Modelo de referencia Rango USD Para qué sirve
Rack abierto 9U–12U StarTech 4POSTRACK12U o similar $200–$350 Organización física, ventilación, cableado
Mini PC potente MinisForum MS-01 (Intel i9-13900H, 64GB RAM, 2TB NVMe) $750–$900 Servidor principal con margen
Mac Mini M4 (opcional) Base, 16GB RAM $599 Si lo usas también de daily driver y para correr LLMs locales con Apple Silicon
Raspberry Pi 5 (8GB) Set completo $130 Servicios ligeros aislados (DNS, dashboard, monitoring)
NAS Synology DS224+ (2 bahías) o DS424+ (4 bahías) $300–$500 + discos Almacenamiento serio con RAID
Discos NAS Seagate IronWolf o WD Red 4TB ×2 $200 Datos del NAS
UPS rackmontable APC SRT1000RMXLI o CyberPower OR1500LCDRM1U $400–$600 Autonomía mayor y formato rack
Switch managed UniFi Flex Mini 2.5G o TP-Link TL-SG2210MP $150–$300 VLANs, monitoreo de tráfico, separación de red
Patch panel 1U + cables Cat6 Genérico 24 puertos $80 Cableado limpio y mantenible
KVM o monitor pequeño Pantalla 7" HDMI + teclado USB $80 Para emergencias sin SSH

Para qué alcanza: Stack completo, modelos grandes en Ollama, almacenamiento RAID, segmentación de red, posibilidad de hospedar servicios para clientes con SLA decente.

Nota práctica: No compres el tier Pro de entrada. Empieza en Intermedio, vive con eso 3–6 meses, y vas a saber exactamente qué te falta antes de gastar más.


📦 3. Stack de servicios — lo que reemplazas

Estos son los doce servicios que cubren la mayoría de lo que un negocio chico necesita. Todos son OSS, todos corren en Docker, todos los he visto en producción.

Servicio self-hosted SaaS que reemplaza Costo evitado/mes USD Imagen Docker
AppFlowy Notion / Confluence $10 appflowyinc/appflowy_cloud
Nextcloud Google Workspace (Drive + Calendar + Contacts) $14 nextcloud:latest
Mattermost Slack Pro (5 usuarios) $44 mattermost/mattermost-team-edition
Vaultwarden Bitwarden / 1Password Business (5 usuarios) $40 vaultwarden/server
DocuSeal DocuSign / HelloSign $25 docuseal/docuseal
Listmonk Mailchimp / ConvertKit $20 listmonk/listmonk
Postiz Buffer / Hootsuite $15 ghcr.io/gitroomhq/postiz-app
n8n Zapier / Make $30 n8nio/n8n
Open WebUI + Ollama ChatGPT Plus $20 ghcr.io/open-webui/open-webui + ollama/ollama
Beszel Datadog lite $15 henrygd/beszel
Cloudflare Tunnel ngrok Pro $20 cloudflare/cloudflared
Homepage Heimdall / Organizr $0 (ambos gratis pero Homepage es mejor) ghcr.io/gethomepage/homepage
Subtotal   ~$253/mes  

Ahorro anual proyectado: ~$3,036 USD.

Tres notas honestas sobre esta tabla:

  1. No todos los reemplazos son 1:1. AppFlowy no tiene tantas integraciones como Notion. Mattermost no tiene la cultura de plugins de Slack. La pregunta correcta no es "¿es idéntico?" sino "¿cubre el 80% que realmente uso?".
  2. Algunos servicios tienen tier gratis en su versión SaaS. Si estás solo y usas Slack free, no te ahorras nada migrando. Esta tabla aplica cuando ya estás pagando.
  3. Tu tiempo cuesta. Mantener esto te va a tomar 1–3 horas al mes. Si tu hora vale $200 y solo te ahorras $50/mes, la cuenta no da.

⚙️ 4. Cómo instalar — paso a paso

Asumimos Tier Intermedio: un mini PC con Ubuntu 24.04 LTS Server instalado, IP fija en tu red local, y SSH habilitado.

Pre-requisitos

  • Ubuntu Server 24.04 LTS instalado (la versión Desktop también funciona).
  • Conexión a Internet estable (idealmente fibra simétrica, mínimo 50/20 Mbps).
  • Un dominio propio (compra uno en Cloudflare Registrar o Namecheap, $10–$15/año).
  • Acceso SSH al servidor desde tu computadora principal.
  • Mínimo 30 minutos de paciencia. Esto no se hace en 5.

Bloque 1 — Instalar Docker y Docker Compose

Conéctate por SSH y corre:

# Actualizar el sistema
sudo apt update && sudo apt upgrade -y

# Dependencias
sudo apt install -y ca-certificates curl gnupg lsb-release

# Repositorio oficial de Docker
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
  sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg

echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
  https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io \
  docker-buildx-plugin docker-compose-plugin

# Tu usuario en el grupo docker (para no usar sudo en cada comando)
sudo usermod -aG docker $USER

# Cierra sesión y vuelve a entrar para que tome el cambio

Verifica:

docker --version
docker compose version
docker run --rm hello-world

Si los tres comandos responden sin errores, ya tienes Docker corriendo.

Bloque 2 — Estructura de directorios

Una carpeta por servicio. Punto. No mezcles configuraciones, no compartas volúmenes entre apps, no improvises.

mkdir -p ~/selfhosted
cd ~/selfhosted

# Una carpeta por servicio
mkdir -p vaultwarden nextcloud mattermost n8n appflowy \
         docuseal listmonk postiz openwebui beszel homepage

Cada carpeta tendrá su propio docker-compose.yml y su carpeta data/ para persistencia. Esto te permite levantar, bajar, respaldar y mover servicios de forma independiente.

Bloque 3 — docker-compose.yml de ejemplo (Vaultwarden)

Vaultwarden es el caso más simple: un solo contenedor, una sola imagen, configuración mínima. Empezar por aquí.

~/selfhosted/vaultwarden/docker-compose.yml:

services:
  vaultwarden:
    image: vaultwarden/server:latest
    container_name: vaultwarden
    restart: unless-stopped
    environment:
      DOMAIN: "https://vault.TU_DOMINIO.com"
      ADMIN_TOKEN: "TU_TOKEN_GENERADO_CON_OPENSSL"
      SIGNUPS_ALLOWED: "false"
      INVITATIONS_ALLOWED: "true"
      WEB_VAULT_ENABLED: "true"
      SMTP_HOST: "smtp.TU_PROVEEDOR.com"
      SMTP_FROM: "vault@TU_DOMINIO.com"
      SMTP_FROM_NAME: "Mi Vault"
      SMTP_PORT: 587
      SMTP_SECURITY: "starttls"
      SMTP_USERNAME: "TU_USUARIO_SMTP"
      SMTP_PASSWORD: "TU_PASSWORD_SMTP"
    volumes:
      - ./data:/data
    ports:
      - "127.0.0.1:8080:80"

Cómo generar cada placeholder:

  • TU_DOMINIO.com: el dominio que compraste. Vaultwarden vivirá en un subdominio, por ejemplo vault.tudominio.com.
  • TU_TOKEN_GENERADO_CON_OPENSSL: corre en tu terminal:
    openssl rand -base64 48
    
    Copia el output completo. Ese es tu admin token. Guárdalo después en el propio Vaultwarden y bórralo del archivo (o muévelo a un .env).
  • SIGNUPS_ALLOWED: "false": importantísimo. Si lo dejas en true cualquiera que conozca la URL puede crearse cuenta.
  • SMTP_*: usa un proveedor real para enviar correos de invitación y reseteo. Opciones: Resend (3,000 emails/mes gratis), Brevo, AWS SES, o tu propio servidor de correo si ya tienes uno.
  • 127.0.0.1:8080:80: importante. Atamos el puerto solo a localhost para que no sea accesible desde la red local. La única forma de llegarle será vía Cloudflare Tunnel, que configuramos en el Bloque 4.

Para levantarlo:

cd ~/selfhosted/vaultwarden
docker compose up -d
docker compose logs -f

Si los logs muestran Rocket has launched from http://0.0.0.0:80, ya está corriendo. Ctrl+C para salir de los logs (no apaga el servicio).

Bloque 4 — Configurar Cloudflare Tunnel

Cloudflare Tunnel te da un endpoint HTTPS público sin abrir puertos en tu router. Es gratis hasta uso comercial respetable y reemplaza ngrok Pro.

a. Crear cuenta gratis en cloudflare.com y verificar el correo.

b. Apuntar tu dominio a Cloudflare. En el dashboard de Cloudflare, agrega tu dominio. Te dará dos nameservers (algo como xxx.ns.cloudflare.com). Vas al panel de tu registrador (donde compraste el dominio) y reemplazas los nameservers actuales por los de Cloudflare. La propagación tarda entre 10 minutos y 24 horas.

c. Instalar cloudflared en el servidor:

# Repositorio oficial
sudo mkdir -p --mode=0755 /usr/share/keyrings
curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg | \
  sudo tee /usr/share/keyrings/cloudflare-main.gpg >/dev/null

echo "deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] \
  https://pkg.cloudflare.com/cloudflared $(lsb_release -cs) main" | \
  sudo tee /etc/apt/sources.list.d/cloudflared.list

sudo apt update
sudo apt install -y cloudflared

d. Autenticarte:

cloudflared tunnel login

Te muestra una URL. Ábrela en tu navegador, selecciona tu dominio, autoriza. Esto guarda un certificado en ~/.cloudflared/cert.pem.

e. Crear el túnel:

cloudflared tunnel create mihomelab

Te devuelve un UUID. Anótalo. Por ejemplo: a1b2c3d4-e5f6-7890-abcd-ef1234567890.

f. Crear el archivo de configuración ~/.cloudflared/config.yml:

tunnel: TU_UUID_DEL_TUNEL
credentials-file: /home/TU_USUARIO/.cloudflared/TU_UUID_DEL_TUNEL.json

ingress:
  - hostname: vault.TU_DOMINIO.com
    service: http://localhost:8080
  - hostname: docs.TU_DOMINIO.com
    service: http://localhost:8081
  - hostname: chat.TU_DOMINIO.com
    service: http://localhost:8082
  - service: http_status:404

La regla final con http_status:404 es obligatoria; es el "catch-all" que Cloudflare exige.

g. Crear los registros DNS automáticamente:

cloudflared tunnel route dns mihomelab vault.TU_DOMINIO.com
cloudflared tunnel route dns mihomelab docs.TU_DOMINIO.com
cloudflared tunnel route dns mihomelab chat.TU_DOMINIO.com

h. Instalar como servicio del sistema:

sudo cloudflared service install

i. Verificar:

sudo systemctl status cloudflared

Debe decir active (running) en verde. Ahora prueba en tu navegador: https://vault.tudominio.com. Si carga la pantalla de login de Vaultwarden, ya tienes Internet de los grandes.

Bloque 5 — Política de backups

La regla 3-2-1 sigue siendo la única que no falla:

  • 3 copias de los datos importantes.
  • 2 medios distintos (disco interno del server + disco externo USB, por ejemplo).
  • 1 copia fuera del sitio (Backblaze B2, AWS S3, o un disco que se queda en otra casa).

Sugerencias prácticas:

  • Diario: snapshot de las carpetas data/ de cada servicio a un disco externo USB con rsync o restic. Cron a las 4 a.m.
  • Semanal: subida cifrada a Backblaze B2 (cuesta $6/TB/mes). restic es la herramienta canónica.
  • Mensual: prueba de restauración. Backup que no se restaura, no es backup, es esperanza.

Ejemplo de script ~/scripts/backup-diario.sh:

#!/bin/bash
set -euo pipefail

SOURCE="$HOME/selfhosted"
DEST="/mnt/backup-externo/selfhosted-$(date +%F)"
LOG="$HOME/scripts/logs/backup-$(date +%F).log"

mkdir -p "$(dirname "$LOG")"

rsync -aAX --delete --exclude='*/data/cache' \
      "$SOURCE/" "$DEST/" >> "$LOG" 2>&1

# Borrar backups con más de 14 días
find /mnt/backup-externo -maxdepth 1 -type d -mtime +14 -exec rm -rf {} \;

chmod +x y agregalo al crontab (crontab -e):

0 4 * * * /home/TU_USUARIO/scripts/backup-diario.sh

🖥️ 5. Dashboard custom

Antes de saltar a Homepage como dashboard final, vale la pena tener uno propio. Aprendes HTML/CSS/JS aplicado, queda exactamente como lo quieres, y se ve bien en una pantalla pequeña montada en la pared al lado del rack.

La captura conceptual

Imagina una pantalla negra de 7 pulgadas (resolución 800×480) montada al lado del switch. En la parte superior izquierda, la hora grande en monoespacio. A su lado, tres badges con CPU, RAM y temperatura del servidor (datos en vivo). Debajo, una rejilla de 3×4 con tarjetas: cada una con el ícono y nombre del servicio (Vaultwarden, Nextcloud, n8n…), un punto de color que indica si está arriba, y un click que abre el servicio en pantalla completa. Tipografía sans-serif, paleta dark con un acento cian. Sin animaciones innecesarias. Información primero.

Stack mínimo

  • nginx sirviendo un único index.html estático.
  • Glances corriendo con la API web (glances -w --bind 127.0.0.1) para CPU, RAM, temperatura.
  • Un archivo HTML con todo embebido (CSS y JS inline, sin frameworks).

Instalar Glances:

sudo apt install -y glances

Arrancarlo como servicio (archivo /etc/systemd/system/glances.service):

[Unit]
Description=Glances API
After=network.target

[Service]
ExecStart=/usr/bin/glances -w --bind 127.0.0.1 --port 61208 --disable-webui
Restart=on-failure

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now glances

Y un nginx mínimo que sirva el HTML y proxee la API de Glances en /api/:

server {
    listen 80;
    server_name dashboard.local;
    root /var/www/dashboard;
    index index.html;

    location /api/ {
        proxy_pass http://127.0.0.1:61208/api/4/;
    }
}

El HTML completo

Guarda este archivo como /var/www/dashboard/index.html. Está listo para copiar y pegar; solo edita el array SERVICES con los tuyos.

<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=800, initial-scale=1">
<title>Home Lab</title>
<style>
  :root {
    --bg: #0b0d10;
    --card: #14181d;
    --border: #1f262d;
    --text: #e6edf3;
    --muted: #7d8590;
    --accent: #38bdf8;
    --ok: #34d399;
    --bad: #f87171;
    --warn: #fbbf24;
    --mono: 'JetBrains Mono', 'SF Mono', Menlo, monospace;
  }
  * { box-sizing: border-box; margin: 0; padding: 0; }
  html, body {
    background: var(--bg);
    color: var(--text);
    font-family: system-ui, -apple-system, sans-serif;
    width: 800px;
    height: 480px;
    overflow: hidden;
  }
  .top {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 12px 16px;
    border-bottom: 1px solid var(--border);
    height: 64px;
  }
  .clock {
    font-family: var(--mono);
    font-size: 32px;
    letter-spacing: 1px;
  }
  .clock .date {
    font-size: 12px;
    color: var(--muted);
    display: block;
    line-height: 1;
  }
  .stats { display: flex; gap: 8px; }
  .badge {
    background: var(--card);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 6px 10px;
    font-family: var(--mono);
    font-size: 12px;
    min-width: 80px;
  }
  .badge .label { color: var(--muted); display: block; font-size: 10px; }
  .badge .value { color: var(--accent); font-size: 16px; font-weight: 600; }
  .grid {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    grid-template-rows: repeat(3, 1fr);
    gap: 8px;
    padding: 8px;
    height: calc(480px - 64px);
  }
  .service {
    background: var(--card);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 12px;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    cursor: pointer;
    transition: border-color 0.15s;
    text-decoration: none;
    color: var(--text);
  }
  .service:hover { border-color: var(--accent); }
  .service .name { font-size: 14px; font-weight: 600; }
  .service .icon { font-size: 24px; margin-bottom: 6px; }
  .service .status {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 11px;
    color: var(--muted);
    font-family: var(--mono);
  }
  .dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--muted);
  }
  .dot.ok   { background: var(--ok);   box-shadow: 0 0 6px var(--ok); }
  .dot.bad  { background: var(--bad);  box-shadow: 0 0 6px var(--bad); }
  .dot.warn { background: var(--warn); box-shadow: 0 0 6px var(--warn); }
</style>
</head>
<body>
  <div class="top">
    <div class="clock">
      <span id="time">--:--:--</span>
      <span class="date" id="date">cargando…</span>
    </div>
    <div class="stats">
      <div class="badge"><span class="label">CPU</span><span class="value" id="cpu">--%</span></div>
      <div class="badge"><span class="label">RAM</span><span class="value" id="mem">--%</span></div>
      <div class="badge"><span class="label">TEMP</span><span class="value" id="temp">--°C</span></div>
    </div>
  </div>
  <div class="grid" id="grid"></div>

<script>
  // Personaliza acá: nombre, URL pública, ícono y endpoint para health-check.
  const SERVICES = [
    { name: 'Vault',      url: 'https://vault.tudominio.com',     icon: '🔐', check: '/alive' },
    { name: 'Nextcloud',  url: 'https://cloud.tudominio.com',     icon: '☁️', check: '/status.php' },
    { name: 'Mattermost', url: 'https://chat.tudominio.com',      icon: '💬', check: '/api/v4/system/ping' },
    { name: 'n8n',        url: 'https://n8n.tudominio.com',       icon: '🔗', check: '/healthz' },
    { name: 'AppFlowy',   url: 'https://docs.tudominio.com',      icon: '📝', check: '/' },
    { name: 'DocuSeal',   url: 'https://firma.tudominio.com',     icon: '✍️', check: '/' },
    { name: 'Listmonk',   url: 'https://news.tudominio.com',      icon: '📨', check: '/health' },
    { name: 'Postiz',     url: 'https://social.tudominio.com',    icon: '📣', check: '/' },
    { name: 'Open WebUI', url: 'https://ai.tudominio.com',        icon: '🤖', check: '/health' },
    { name: 'Beszel',     url: 'https://monitor.tudominio.com',   icon: '📊', check: '/' },
    { name: 'Glances',    url: 'http://tu-ip-local:61208',        icon: '⚡', check: '/' },
    { name: 'Logs',       url: 'http://tu-ip-local:9999',         icon: '📋', check: '/' }
  ];

  const grid = document.getElementById('grid');
  SERVICES.forEach((s, i) => {
    const a = document.createElement('a');
    a.className = 'service';
    a.href = s.url;
    a.target = '_blank';
    a.innerHTML = `
      <div>
        <div class="icon">${s.icon}</div>
        <div class="name">${s.name}</div>
      </div>
      <div class="status">
        <span class="dot" id="dot-${i}"></span>
        <span id="lat-${i}">checking…</span>
      </div>`;
    grid.appendChild(a);
  });

  function tick() {
    const d = new Date();
    document.getElementById('time').textContent =
      d.toLocaleTimeString('es-MX', { hour12: false });
    document.getElementById('date').textContent =
      d.toLocaleDateString('es-MX', { weekday: 'short', day: '2-digit', month: 'short' });
  }
  tick(); setInterval(tick, 1000);

  async function pullStats() {
    try {
      const cpu  = await (await fetch('/api/cpu')).json();
      const mem  = await (await fetch('/api/mem')).json();
      const sens = await (await fetch('/api/sensors')).json();
      document.getElementById('cpu').textContent  = Math.round(cpu.total) + '%';
      document.getElementById('mem').textContent  = Math.round(mem.percent) + '%';
      const tempSensor = (sens || []).find(s => /package|cpu/i.test(s.label));
      document.getElementById('temp').textContent =
        tempSensor ? Math.round(tempSensor.value) + '°C' : '--°C';
    } catch (e) { /* dashboard offline = silencio */ }
  }
  pullStats(); setInterval(pullStats, 5000);

  async function checkServices() {
    SERVICES.forEach(async (s, i) => {
      const dot = document.getElementById('dot-' + i);
      const lat = document.getElementById('lat-' + i);
      const t0 = performance.now();
      try {
        await fetch(s.url + s.check, { mode: 'no-cors', cache: 'no-store' });
        const ms = Math.round(performance.now() - t0);
        dot.className = 'dot ' + (ms > 1500 ? 'warn' : 'ok');
        lat.textContent = ms + ' ms';
      } catch {
        dot.className = 'dot bad';
        lat.textContent = 'down';
      }
    });
  }
  checkServices(); setInterval(checkServices, 30000);
</script>
</body>
</html>

Cómo personalizarlo

  • Servicios: edita el array SERVICES. Cada objeto tiene name, url, icon (un emoji o un carácter Unicode) y check (un endpoint que responda 200 cuando el servicio está bien).
  • Colores: todo vive en las variables CSS al tope del archivo (--bg, --card, --accent, etc.). Cambiar --accent te re-tematiza el dashboard entero.
  • Resolución: el body está clavado a 800×480 porque está pensado para un panel de 7". Si lo vas a montar en una pantalla más grande, quita esos width y height del html, body y deja que la grid se expanda.
  • Health-check: si tus servicios no exponen un /health, usa simplemente /. El navegador no verá la respuesta (CORS) pero medirá si responde y en qué tiempo.

🛡️ 6. Recomendaciones operacionales

Tres cosas que SIEMPRE hacer

  1. Backups automáticos y probados. No es backup hasta que restauraste con éxito desde él al menos una vez. Marca un sábado al mes en el calendario para hacer un drill de restauración. Suena exagerado hasta que se te muere el SSD.
  2. Monitoreo activo. Beszel, Uptime Kuma o el dashboard custom sirven. Lo importante es enterarte de que algo falló antes que el usuario (que en muchos casos eres tú mismo). Configura alertas por correo o Telegram.
  3. No expongas puertos al WAN. Cloudflare Tunnel resuelve esto sin esfuerzo. Si abres puertos en tu router, en menos de 24 horas tienes a bots de medio mundo intentando entrar a tu SSH y a tus paneles. Es ley.

Tres errores comunes de principiantes

  1. Usar :latest para todo y no fijar versiones. Un día actualizas, la imagen cambió de mayor versión, la base de datos no es compatible, y pierdes data. Usa tags específicos (vaultwarden/server:1.32.7) y sube versiones a propósito, leyendo el changelog.
  2. Reusar la misma contraseña en root, en el panel del router y en los servicios. Esto es 2026, ya no hay excusa. Vaultwarden lo arregla.
  3. No documentar nada. A los seis meses no te vas a acordar de por qué pusiste ese parámetro raro en el docker-compose.yml. Mantén un README en cada carpeta con: para qué sirve el servicio, cómo se instaló, decisiones extrañas, y comandos útiles. Tu yo del futuro te lo agradece.

Cómo escalar cuando se te queda corto

  • Más RAM/CPU: pasar de mini PC con N100 a uno con i5/i9 (MS-01) duplica o triplica capacidad.
  • Más almacenamiento: agrega un NAS Synology y mueve los volúmenes pesados (Nextcloud, backups, archivos de usuarios) ahí vía NFS.
  • Alta disponibilidad: monta dos servidores y orquesta con Docker Swarm o k3s. Esto ya es otro mundo y otro nivel de complejidad; no lo hagas hasta que de verdad lo necesites.
  • Aislamiento de red: switch managed con VLANs separadas para servicios públicos (vía Cloudflare), red interna, e IoT. El día que un dispositivo IoT chino se reporte malicioso, vas a agradecer la VLAN.

📑 7. Cierre

Tabla resumen — inversión vs ahorro

Tier Inversión hardware Ahorro mensual estimado Ahorro anual Punto de equilibrio
Starter ~$200 $50–$80 $600–$960 3–4 meses
Intermedio ~$600 $150–$253 $1,800–$3,036 3–6 meses
Pro ~$1,500–$2,500 $250–$500+ $3,000–$6,000+ 6–10 meses

A todo esto súmale: aprendizaje técnico, control de tus datos, y la satisfacción específica de apagar cuatro suscripciones SaaS en una tarde de domingo.

Disclaimer

Este documento es material educativo. No reemplaza soporte técnico personalizado ni consultoría. Las prácticas descritas aquí asumen que el lector tiene capacidad de evaluar su propio caso, los riesgos de operar infraestructura, y el cumplimiento legal y regulatorio que aplique en su jurisdicción (LGPD, GDPR, leyes locales de protección de datos, etc.).

Las marcas y productos mencionados pertenecen a sus respectivos dueños. No tengo relación comercial ni de afiliado con ninguno de ellos en el contexto de este documento.


Lista de compra — Mi Home Lab

Este es el hardware exacto del rack que aparece en el video. Si estás empezando, no tienes que comprar todo de un solo viaje: arranca por el mini PC y el switch, y ve sumando cuando lo necesites.

# Componente Modelo de referencia Para qué sirve Link
1 Rack

GeeekPi 12U Server Cabinet, 10 inch Server Rack for Network

Mueble metálico que organiza el hardware en altura https://amzn.to/3OWH47Y
2 Power

10 inch Rack PDU, 4 Rear Outlets, 1020J Surge Protection

Fuente de Poder https://amzn.to/42M7We1
3 Patch panel

GeeekPi 12 Port Patch Panel, 10inch 0.5U CAT6 Network Patch Panel for DeskPi RackMate

Centraliza el cableado de red al rack https://amzn.to/4u1FjFy
4 Switch

TP-Link 8 Port Gigabit Switch | Easy Smart Managed

Conecta los equipos a la red local https://amzn.to/4tdIhWs
5 Mini PC

Beelink SER9 Pro AI Mini PC, AMD Ryzen 7 H 255(8C/16T,4.9GHz)

Servidor principal — corre todos los contenedores Docker https://amzn.to/4w9TxpA
6 Mac Mini M4 Base, 16GB RAM Daily driver para trabajo y modelos locales con Apple Silicon out of stock 
7 Synology NAS

DS223j

Almacenamiento y backups de todos los sistemas https://amzn.to/4tj1h5L
8 Raspberry Pi 5 (8GB) Kit oficial Servicios ligeros aislados (DNS, dashboard, monitoreo) https://amzn.to/4dmELnP

Notas prácticas: El NAS Synology y el mini PC se venden en varias configuraciones. Verifica si tu unidad necesita componentes adicionales (discos NAS-grade, RAM extra) antes de checkout. La Raspberry Pi se vende como placa sola o como kit con caja, fuente y microSD; para empezar limpio, ve por el kit.

Changelog

  • v1.0 — 2026-05-04: Versión inicial. Cubre tres tiers de hardware, doce servicios self-hosted, instalación de Docker, ejemplo completo de Vaultwarden, configuración de Cloudflare Tunnel, dashboard custom, y recomendaciones operacionales.

Únete a la Comunidad

Regístrate gratis para descargar archivos, guardar recursos en favoritos, ganar XP y acceder a cursos y el foro de la comunidad.

¿Ya tienes cuenta? Inicia sesión

Erik Taveras

Autor

Erik Taveras

Recursos Relacionados

Tutorial
Portada de Guía VARIANTES - Las 8 versiones alternativas de OpenClaw

Guía VARIANTES - Las 8 versiones alternativas de OpenClaw

Esta es la guía que te prometí en el reel. Aquí tienes las ocho versiones alternativas a OpenClaw que están circulando ahora mismo, con datos reales — lenguaje, requerimientos, comando de instalación, link al repositorio y para qué tipo de proyecto sirve cada una. Al final hay tres preguntas para que decidas cuál te conviene antes de instalar la primera.

1353 0