🏠 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:
- 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?".
- 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.
- 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
- 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.
- 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.
- 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
- 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.
- 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.
- 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.