Home Lab para Agentes IA con Ollama y Proxmox en Producción: Respaldos, Observabilidad y Failover
Por qué este post
El post anterior de esta serie (Creando tu Home Lab para Agentes IA: Ollama + Proxmox en 2026) cubrió el setup inicial: instalar Proxmox 9 sobre Debian 13, pasar la GPU al LXC privileged, levantar Ollama 0.40 dentro del contenedor, exponer el endpoint OpenAI-compatible y conectarle Open WebUI + LiteLLM + Tailscale. Esa fue la parte divertida y termina con un agente que responde. Esta guía es la aburrida y necesaria que viene después: cómo operar ese homelab durante meses sin que se rompa a las tres de la mañana, cómo recuperarte cuando se rompe, y cómo saber que algo se está degradando antes de que el usuario final se queje.
La diferencia entre un homelab IA de fin de semana y un homelab IA en producción son tres prácticas: respaldos que se prueban restaurando, observabilidad que convierte los registros en alertas accionables, y failover que decide por ti cuando un nodo se cae en mitad de una inferencia. El setup te da el lujo de tener un agente corriendo. La operación es lo que te permite dormir tranquilo mientras corre. Esta guía asume que ya tienes el stack del post anterior funcionando; si no, léelo primero, vuelve acá, y vas a poder aplicar todo lo que sigue sobre tu propia instalación en una tarde.
TL;DR — Lo que vas a tener al final
TL;DR — Lo que vas a tener al final
- Tiempo de setup: 4-6 horas la primera vez (1-2 horas ya con la práctica), asumiendo que el post de setup ya está aplicado.
- Requisitos: Proxmox VE 9.x con cluster de 2+ nodos (o un nodo + PBS en otra máquina), segunda máquina o VM para Proxmox Backup Server, Ollama 0.40 o superior corriendo en LXC, 50 GB libres en el pool de almacenamiento para snapshots, grafana + Prometheus ya instalados o por instalar.
- Resultado: respaldos verificados del LXC de Ollama + modelos completos + base de Open WebUI, métricas de GPU/OLLAMA/cola/promedio de tokens exportadas a Prometheus con dashboards de Grafana, alertas por temperature de GPU y latency p99, y plan de failover probado con HA de Proxmox reiniciando tu LXC en otro nodo cuando el primario cae.
- Troubleshooting: snapshot del LXC incluyendo VRAM no sirve porque la VRAM es stateful del kernel de Proxmox, no del filesystem; restic reporta “tree node not found” cuando el repo se interrumpe a mitad de subida (se repara con
restic repair); DCGM exporter devuelve “Permission denied” cuando el contenedor LXC no tiene el capabilitySYS_ADMIN; HA failover reinicia el LXC en otro nodo pero pierde los modelos que estaban cargados en VRAM, lo que dispara una nueva carga de 30-90 segundos.
El homelab que ya tienes como punto de partida
Antes de tocar nada, dejame dibujar exactamente lo que se asume que ya está funcionando. Esta guía no duplica el setup inicial — parte de un stack que ya tiene estas piezas, las mismas que montamos en la primera entrega, y agrega la capa operacional encima:
| Capa | Componente | Estado esperado al empezar esta guía |
|---|---|---|
| Hypervisor | Proxmox VE 9.0 o superior sobre Debian 13 | Instalado con cluster de un nodo (single-node) o 2-3 nodos miembros del cluster corosync |
| GPU | RTX 3060 12 GB / RTX 4090 24 GB / RTX 3090 24 GB | Pasada al LXC ollama con lspci -k -s <gpu> mostrando vfio-pci como driver |
| Inferencia | Ollama 0.40.x dentro del LXC privileged | Sirviendo en 0.0.0.0:11434, con al menos un modelo 7B-13B descargado en /var/lib/ollama |
| UI / Gateway | Open WebUI + LiteLLM en LXC separado | Conectado al endpoint Ollama, accesible vía Tailscale o LAN |
| Exposición | Tailscale o Caddy reverse proxy | Operativo, sin puertos WAN abiertos si es Tailscale |
Si te falta cualquiera de esas piezas, el orden importa: primero el setup, después el PBS en una máquina separada, después los snapshots del LXC, después la observabilidad. Saltarse el PBS antes de cualquier backup es la causa número uno de “el fin de semana se arruinó porque cambié algo en el LXC y rompí el Ollama”. Todo lo que viene asume ese estado y construye sobre él.
Diseña el plan de respaldos según qué se pierde si desaparece
El error más común es tratar los respaldos del homelab IA como una sola cosa y meter todo en un tar.gz. No son una sola cosa. Cada componente tiene un costo distinto de regenerar y un RTO (Recovery Time Objective) distinto tolerable. Antes de configurar nada, vale la pena enumerar qué estás protegiendo, qué tan crítico es, y cuánto tiempo aceptas que tu agente esté caído si ese componente desaparece.
La taxonomía que mejor me ha funcionado tras varios incidentes reales en mi propio stack:
| Componente | Tamaño típico | Costo si se pierde | Método de respaldo | Frecuencia |
|---|---|---|---|---|
| LXC de Ollama (filesystem completo) | 20-40 GB | Alto: hay que reinstalar drivers y re-configurar systemd overrides | Snapshot Proxmox via vzdump | Diario, retención 7 snapshots |
Modelos en /var/lib/ollama |
50-150 GB | Medio: solo se bajan de nuevo, pero son 30 GB de descarga por modelo y la primera inferencia tarda más | restic a PBS, con deduplicación por capas | Semanal, retención 4 semanas |
| Modelfiles y configs de Open WebUI | 50-500 MB | Bajo: reconstruible en horas | Git repo en el home del agente | Cada cambio → git push |
| Base de datos SQLite de Open WebUI | 10 MB – 2 GB | Alto si tienes conversaciones y RAG cargados: pierdes histórico y embeddings | restic incremental + verificación de integridad | Cada 6 horas si el uso es alto |
| Definiciones de Prometheus / Grafana | 10-100 MB | Medio: pierdes dashboards custom y reglas de alertas | Export vía API de Grafana + repositorio git | Diario |
| Tokens y credenciales (Tailscale, OLLAMA_AUTH) | < 1 MB | Catastrófico si se pierden todas las copias: tu agente queda expuesto sin protección | 1Password / bitwarden + copia en papel si es el único acceso | Cada cambio |
La primera columna es la decisión clave: el LXC entero va con snapshot Proxmox (rápido, completo, recuperable en 5 minutos con qmrestore), los modelos van con restic a PBS (lento pero dedup-friendly para los 30 GB de cada modelo cuantizado en Q4_K_M), y los Modelfiles/Configs/Tokens van en Git porque son texto y la auditoría es lo importante. Mezclar esas categorías termina en respaldos de 200 GB que se rompen o en “hice git pull pero olvidé el snapshot del LXC”.
Snapshots del LXC con vzdump y el datastore PBS
Proxmox Backup Server (PBS) versión 4.2, liberada el 29 de abril de 2026, ya soporta S3-compatible storage como destino, lo que cambia las opciones para el homelab que no tiene segunda máquina dedicada. Si tienes una Synology o un NAS con capacidad de correr VMs ligeras, PBS vive ahí; si no, una mini-PC usada de 8 GB RAM con un disco grande hace el papel perfectamente. En mi setup uso un Lenovo M720q refurbado con un SSD de 1 TB dedicado, dado que el SSD del Proxmox primario no puede ser destino de sus propios backups (regla dura del manual de PBS: nunca respaldar un host sobre sí mismo).
El primer paso es agregar el PBS como storage en el Datacenter de Proxmox. Desde la UI es Datacenter → Storage → Add → Proxmox Backup Server; por CLI:
# En el Proxmox primario, como root
pvesm add pbs pbs-homelab
--backend pbs
--host 192.168.5.50
--datastore ollama-backups
--username backup@pbs
--password "$(cat /etc/pve/priv/storage/pbs-homelab.pwd)"
--encryption-key "$(cat /etc/pve/priv/storage/pbs-homelab.enc)"
--prune-backups keep-daily=7,keep-weekly=4,keep-monthly=6
--max-workers 2
El flag --encryption-key cifra los backups en el lado del cliente antes de subirlos al PBS. Si te roban el disco del PBS o un atacante accede al datastore, los snapshots son ilegibles sin esa clave. Guarda la encryption key FUERA del PBS: en un pendrive cifrado, en 1Password, en papel dentro de un sobre. Perder la clave significa que tus backups existen pero son inútiles. Una vez configurado el storage, programar los snapshots es trivial desde la UI o por CLI:
# Crear un job que respalda el LXC 100 (ollama) cada noche a las 03:00
vzdump 100
--storage pbs-homelab
--mode snapshot
--compress zstd
--mailnotification failure
--notification-mode notification-system
# Agregar al cron del sistema
cat >> /etc/cron.d/vzdump-ollama <<'EOF'
0 3 * * * root /usr/sbin/vzdump --quiet 100 --storage pbs-homelab --mode snapshot --compress zstd --mailnotification failure
EOF
El --mode snapshot hace el respaldo con el LXC corriendo, sin downtime. Proxmox usa el snapshot de ZFS (o LVM-thin) y los chunks que cambian entre ejecuciones se dedupican automáticamente contra la versión anterior, así que el segundo snapshot diario pesa solo lo que cambió en las últimas 24 horas, no los 40 GB enteros.
Restic para los modelos: dedup real en archivos enormes
Los modelos cuantizados en Q4_K_M pesan entre 4 GB (un 7B) y 60 GB (un 70B). Si haces un respaldo completo de /var/lib/ollama cada semana, en pocas semanas estás pagando por subir cientos de gigabytes que no cambian. Restic aplica content-defined chunking y deduplica bloques a nivel binario, no a nivel archivo: si solo cambian 200 MB de los 60 GB de un modelo grande, restic sube esos 200 MB y reutiliza el resto. La versión 0.19.1 (julio 2026) añadió manejo de S3 multipart upload más robusto, lo que hace que el destino sea PBS-vía-S3, un bucket de Wasabi o Cloudflare R2 indistinguible desde el punto de vista del script.
El script que tengo corriendo como root dentro del LXC de Ollama, vía cron, cada domingo a las 04:00:
#!/usr/bin/env bash
set -euo pipefail
export AWS_ACCESS_KEY_ID="restic-pbs-key"
export AWS_SECRET_ACCESS_KEY="restic-pbs-secret"
export RESTIC_REPOSITORY="s3:s3.us-east-1.wasabisys.com/pbs-ollama-models"
export RESTIC_PASSWORD_FILE="/etc/restic/passes/ollama-models.pass"
# Restic deduce el endpoint del prefijo s3:, pero Wasabi necesita region explícita:
export AWS_DEFAULT_REGION="us-east-1"
restic backup /var/lib/ollama
--tag ollama-models
--exclude-caches
--exclude ".ollama/models/manifests/registry.ollama.ai/**/.*"
--pack-size 64
--compression max
# Verificar la integridad del último snapshot
restic check --read-data-subset=5%
# Forget policy: 4 semanales, 6 mensuales, 0 anuales
restic forget --keep-weekly 4 --keep-monthly 6 --prune
El parámetro --pack-size 64 sube el tamaño de cada bloque a 64 MB, lo que mejora el throughput cuando los modelos son archivos grandes y consecutivos. --exclude-caches evita que restic pierda tiempo mirando blobs intermedios que Ollama regenera en cada pull. La verificación --read-data-subset=5% lee el 5% aleatorio de cada snapshot — es la red de seguridad contra corrupción silenciosa del datastore, y cuesta pocos minutos en un dataset de 100 GB. Sin esa verificación, un día intentas restaurar un modelo y descubres que el snapshot está roto desde hace tres meses.
Probar el restore antes de necesitarlo
Un respaldo que nunca se ha restaurado es un respaldo que no existe. La regla de operación es: cada tres meses, restauración de prueba a un LXC con VMID distinto, verificación de que Ollama arranca y responde, eliminación del LXC de prueba. Hacerlo manual no escala; mejor un script que se corra vía cron y mande un reporte a Telegram con el resultado.
#!/usr/bin/env bash
set -euo pipefail
DATE=$(date +%Y%m%d)
LOG="/tmp/restore-test-${DATE}.log"
echo "Restore test started at $(date)" | tee "$LOG"
# Buscar el último snapshot de vzdump del LXC 100
SNAP=$(ssh root@pbs "/opt/pbs/bin/proxmox-backup-manager snapshot list --repository ollama-backups --backup-type ct --backup-id 100" | head -1 | awk '{print $1}')
if [ -z "$SNAP" ]; then
echo "No snapshot found for CT 100" | tee -a "$LOG"
exit 1
fi
ssh root@pbs "/opt/pbs/bin/proxmox-backup-manager restore ct/100/${SNAP} --target-host pve-prod --target-vmid 999" 2>&1 | tee -a "$LOG"
# Esperar a que arranque
sleep 30
# Verificar que Ollama responde
RESULT=$(ssh root@pve-prod "pct exec 999 -- curl -sf http://localhost:11434/api/tags | jq '.models | length'")
if [ "$RESULT" -gt 0 ]; then
echo "Restore test PASSED: $RESULT models available" | tee -a "$LOG"
# Limpiar el LXC de prueba
ssh root@pve-prod "pct stop 999; pct destroy 999"
# Mandar reporte de éxito
curl -s "https://api.telegram.org/bot${TELEGRAM_TOKEN}/sendMessage"
-d "chat_id=${TELEGRAM_CHAT_ID}"
-d "text=✅ Restore test OK: $RESULT modelos verificados el $(date)"
else
echo "Restore test FAILED: Ollama did not respond correctly" | tee -a "$LOG"
curl -s "https://api.telegram.org/bot${TELEGRAM_TOKEN}/sendMessage"
-d "chat_id=${TELEGRAM_CHAT_ID}"
-d "text=❌ Restore test FAIL: revisar $LOG"
exit 1
fi
El script es austero a propósito: si algo se rompe, el log en /tmp y el mensaje a Telegram son la única señal. La parte de Telegram se puede reemplazar por un correo, un ticket en n8n o un push a ntfy.sh — lo importante es que el resultado NUNCA quede solo en el log del cron. La historia del restore-test que falla silenciosamente durante seis meses y se descubre cuando realmente necesitas restaurar es demasiado común como para no automatizar.
Observabilidad: lo que necesitas medir y lo que NO
Medir todo es la mejor receta para gastar RAM y no enterarte cuando algo falla. Las métricas que importan en un homelab IA son cuatro categorías, todas exportables a Prometheus con exporters ya mantenidos por la comunidad: salud de la GPU, salud del motor Ollama, throughput real, y errores de cola. El resto (métricas del kernel del LXC, tráfico de red, I/O del SSD) tiene cobertura razonable con el node-exporter genérico y dashboards comunitarios; no reinventes la rueda.
| Categoría | Métrica clave | Exporter | Dashboard Grafana |
|---|---|---|---|
| GPU | DCGM_FI_DEV_GPU_TEMP, DCGM_FI_DEV_GPU_UTIL, DCGM_FI_DEV_FB_USED, DCGM_FI_DEV_FB_FREE |
DCGM-Exporter 4.8.x (port 9400, imagen nvcr.io/nvidia/k8s/dcgm-exporter) | ID 25193 Neurix Ollama + NVIDIA |
| Ollama | tokens/segundo por modelo, modelos cargados, queue depth, latency p50/p95/p99 | frcooper/ollama-exporter o maravexa/ollama-exporter (port 9401) | ID 25086 Ollama LLM Inference |
| Sistema | CPU%, RAM%, disk I/O wait, network drops | node-exporter (port 9100) | ID 1860 Node Exporter Full |
| Aplicación agente | request count, error rate, time-to-first-token | Open WebUI tiene stats embebidas, o instrumentas con prometheus_client en Python |
Custom, 5 paneles |
DCGM-Exporter en el LXC de Ollama
La forma soportada por NVIDIA de exponer métricas de GPU es DCGM-Exporter, mantenido activamente (versión 4.8.4 publicada en septiembre de 2026). Necesita el capability SYS_ADMIN para acceder a los registros de DCGM, lo cual requiere una configuración específica del LXC en Proxmox. Si lo corres como container Docker dentro del LXC, agregá --cap-add SYS_ADMIN al docker run; si lo corres nativo, asegurate de que el contenedor LXC tenga habilitada la opción Nesting y Features → keyctl.
# En el LXC de Ollama, como root
apt-get update
apt-get install -y datacenter-gpu-manager
# Arrancar el exporter como servicio
mkdir -p /opt/dcgm-exporter
docker run -d
--name dcgm-exporter
--restart unless-stopped
--network host
--gpus all
--cap-add SYS_ADMIN
nvcr.io/nvidia/k8s/dcgm-exporter:4.8.4
--collectors /etc/dcgm-exporter/dcp-metrics-included.csv
# Validar que responde
sleep 10
curl -s http://localhost:9400/metrics | grep DCGM_FI_DEV_GPU_TEMP | head -3
# Esperado:
# DCGM_FI_DEV_GPU_TEMP{gpu="0",UUID="GPU-...",modelName="NVIDIA RTX 4090",...} 47.0
El flag --network host es deliberado: el exporter comparte el namespace de red del LXC y queda escuchando en localhost:9400, accesible desde Prometheus que corre en otra máquina. Si lo exponés en una IP pública por accidente, la métrica incluye nombres de modelos y serial numbers de GPUs, que son información razonable para inventario pero no para el mundo exterior.
Ollama-exporter: tokens/segundo por modelo
El Ollama principal no expone un endpoint Prometheus nativo todavía (hay un RFC abierto pero no entra en la 0.41). El workaround es correr un sidecar que scrapea el endpoint /api/ps de Ollama cada 10 segundos y lo convierte a métricas Prometheus. Hay dos implementaciones maduras: frcooper/ollama-exporter y maravexa/ollama-exporter. La segunda incluye un panel Grafana pre-armado que se ve bonito pero la primera es más configurable. Yo uso la primera.
# Compilar desde Go en la máquina donde corre Prometheus, o como binario portable
go install github.com/frcooper/ollama-exporter/cmd/exporter@latest
ollama-exporter --ollama-url http://192.168.5.51:11434 --listen :9401 &
# Verificar
curl -s http://localhost:9401/metrics | grep ollama_ | head -10
# Esperado:
# ollama_models_loaded{...} 2
# ollama_tokens_per_second{model="qwen3:14b",...} 18.4
# ollama_request_duration_seconds_bucket{model="qwen3:14b",...} 0.85
# ollama_queue_size{...} 1
La métrica más valiosa es ollama_tokens_per_second con label de modelo. Te dice qué tan rápido está respondiendo tu agente en este momento, separado por modelo. Si tienes dos LXC de Ollama con modelos distintos, ves cuál está sobrecargado y cuál está frío. La métrica ollama_request_duration_seconds_bucket te da p50/p95/p99 reales, que es lo que tu agente percibe como latencia — los SLA se calculan sobre eso, no sobre tiempo de respuesta del kernel.
Prometheus: scrape config mínimo
Prometheus corre en otro LXC o máquina (nunca en el mismo LXC que Ollama, por la contención de recursos). El config mínimo que necesito para el homelab IA:
# /etc/prometheus/prometheus.yml
global:
scrape_interval: 15s
evaluation_interval: 30s
scrape_configs:
- job_name: 'ollama-gpu'
static_configs:
- targets: ['192.168.5.51:9400'] # DCGM exporter en el LXC de Ollama
labels: {host: ollama-prod-01, gpu: 'RTX-4090'}
- job_name: 'ollama-api'
static_configs:
- targets: ['192.168.5.51:9401'] # ollama-exporter
labels: {host: ollama-prod-01}
- job_name: 'ollama-node'
static_configs:
- targets: ['192.168.5.51:9100'] # node-exporter
labels: {host: ollama-prod-01}
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
El job ollama-gpu falla cuando DCGM-Exporter se cae: usualmente porque el contenedor del exporter perdió el acceso a /dev/nvidia0 después de un reinicio del LXC. La métrica up == 0 es tu alerta silenciosa de que perdiste observabilidad de la GPU. Configurar el servicio como --restart unless-stopped lo recupera solo, pero el alerta sigue siendo válida.
Alertas: cuatro reglas que importan, ninguna más
La trampa típica de un homelab IA es configurar alertas para todo y después ignorarlas todas. Las cuatro reglas que de verdad importan en mi setup, en orden de severidad, son las siguientes. Cada una es una combinación de métrica exportada + threshold que aprendí a la mala. El formato es para Prometheus Alertmanager, y se aplican vía promtool check rules antes de commit.
# /etc/prometheus/rules/ollama-alerts.yml
groups:
- name: ollama-critical
rules:
- alert: GPUTempHigh
expr: DCGM_FI_DEV_GPU_TEMP > 85
for: 2m
labels: {severity: critical, team: homelab-ia}
annotations:
summary: "GPU {{ $labels.gpu }} caliente ({{ $value }}°C)"
description: "Reducir carga o revisar ventilación. Típicamente el problema es el cooler bloqueado o el cuarto sin aire acondicionado."
- alert: OllamaNotResponding
expr: up{job="ollama-api"} == 0
for: 1m
labels: {severity: critical, team: homelab-ia}
annotations:
summary: "Ollama exporter caído en {{ $labels.host }}"
description: "Ollama probablemente está colgado. Revisar 'ollama ps' y journalctl -u ollama."
- alert: ModelLoadTimeoutExceeding
expr: increase(ollama_model_load_duration_seconds_count[10m]) > 5
for: 0m
labels: {severity: warning, team: homelab-ia}
annotations:
summary: "{{ $value }} cargas de modelo en 10 min en {{ $labels.host }}"
description: "Probable cause: keep_alive bajo + múltiples modelos alternándose. Considerar subir OLLAMA_KEEP_ALIVE a -1."
- name: ollama-warning
rules:
- alert: LatencyP95High
expr: histogram_quantile(0.95, sum(rate(ollama_request_duration_seconds_bucket[5m])) by (le, model)) > 8
for: 5m
labels: {severity: warning, team: homelab-ia}
annotations:
summary: "Latencia p95 > 8s en modelo {{ $labels.model }}"
description: "Verificar OLLAMA_NUM_PARALLEL, VRAM disponible con DCGM_FI_DEV_FB_FREE, y si hay modelos compitiendo."
- alert: QueueDepthGrowing
expr: ollama_queue_size > 8
for: 2m
labels: {severity: warning, team: homelab-ia}
annotations:
summary: "Cola de Ollama > {{ $value }} requests esperando"
description: "Subir OLLAMA_MAX_QUEUE o reducir concurrency upstream. Si persistió, el modelo probablemente no cabe en VRAM y se está paginando."
Cada alerta tiene un destino: las dos críticas van a Telegram con un sonido distintivo, las dos warnings van a un canal silencioso que reviso los lunes. El patrón “alertas críticas que suenan, warnings en un dashboard pasivo” mantiene mi teléfono sin vibrar a las 3 AM por cosas que resuelven solas, y me despierta para las que sí importan. La alerta ModelLoadTimeoutExceeding la aprendí cuando tres agentes distintos hicieron ollama pull al mismo modelo en distintos horarios: cada pull evictaba el modelo del cache, y el siguiente request lo recargaba de disco a 600 MB/s. La métrica que delata el patrón es exactamente el contador de cargas por ventana de tiempo.
Failover: HA de Proxmox para tu LXC de Ollama
Proxmox VE HA (High Availability) no es live migration. Esa es la primera distinción que hay que entender antes de configurar nada. Live migration mueve un LXC en caliente de un nodo a otro sin reiniciar; HA failover, cuando el nodo primario se cae, reinicia automáticamente el LXC en el nodo secundario. La diferencia operativa: en live migration no perdés los modelos cargados en VRAM; en HA failover, sí. Esto significa que cada failover paga el costo de recargar el modelo a 30-90 segundos según el tamaño.
El costo es aceptable si el nodo primario cayó por algo grave (hardware fallando, kernel panic). No es aceptable si lo configuraste pensando que era live. La decisión clave es: HA failover es tu red de seguridad, no tu modo primario de operación.
Habilitar HA en un LXC existente
El procedimiento es de tres comandos, todos como root en uno de los nodos del cluster:
# Confirmar que el cluster tiene al menos 2 nodos miembros
pvecm status
# Si solo hay un nodo, ver la nota al final de esta sección
ha-manager add ct:100
--group homelab-ia
--max-restart 3
--max-relocate 3
--state started
# Verificar
ha-manager status
# El LXC 100 debe aparecer como "started" en el nodo actual
Los flags --max-restart 3 y --max-relocate 3 controlan cuántas veces Proxmox intenta recuperar el LXC antes de declararlo “permanent failure” y dejar de gastar recursos. Para un homelab donde los LXC tienen menos estado crítico que una VM de base de datos, 3 intentos es razonable. Después de tres fallos consecutivos, Proxmox deja el LXC apagado y manda una alerta para que intervengas manualmente.
Lo que se pierde en un failover y cómo mitigarlo
El failover real implica cuatro pérdidas predecibles, cada una con su mitigación:
| Se pierde | Mitigación | Tiempo de recuperación |
|---|---|---|
| Modelo cargado en VRAM | OLLAMA_KEEP_ALIVE=-1 para reducir probabilidad, pero el failover la limpia igual |
30-90 segundos según tamaño del modelo |
| Conexiones TCP activas (Open WebUI, agentes externos) | Usar retry exponencial en el cliente; configurar LiteLLM como proxy upstream con health check | 1-5 segundos |
| Estado de la conversación en Open WebUI | La DB es SQLite en disco; el failover no toca el FS; se recupera al volver | 0 segundos (si el ZFS sync estaba al día) |
| Promedio de tokens/segundo (métrica) | Nada que hacer; el exporter se reconecta solo cuando Ollama vuelve | 5-10 segundos |
El patrón que mejor funciona en producción es poner LiteLLM entre el cliente y Ollama. LiteLLM hace health checks contra Ollama con retries exponenciales. Cuando Ollama se cae y vuelve, LiteLLM nota la caída en su próximo health check (3-5 segundos) y empieza a rutear tráfico de nuevo, ocultando el failover del cliente. Esto convierte un failover de 30-90 segundos en uno de 3-5 segundos para el usuario final.
Cluster de un solo nodo: el caso real del homelab
La mayoría de homelabs reales arrancan como single-node, y agregar un segundo nodo Proxmox es caro si ya invertiste en GPU. La opción intermedia es HA local: poner el LXC de Ollama en un ZFS subvol con --onboot 1 --startup order=1,up=10 y un watchdog que reinicie el LXC si Ollama deja de responder. Eso NO te protege de kernel panic del host, pero sí de los fallos más comunes (OOM, crash del modelo, deadlock del scheduler).
# Como root en el Proxmox host
cat > /usr/local/bin/ollama-healthcheck.sh <<'EOF'
#!/usr/bin/env bash
# Reinicia el LXC si Ollama no responde en 30 segundos
if ! pct exec 100 -- curl -sf --max-time 30 http://localhost:11434/api/tags >/dev/null 2>&1; then
echo "$(date) - Ollama no responde, reiniciando LXC 100" | tee -a /var/log/ollama-health.log
pct stop 100 --timeout 60
sleep 5
pct start 100
fi
EOF
chmod +x /usr/local/bin/ollama-healthcheck.sh
# Cada minuto
echo "* * * * * root /usr/local/bin/ollama-healthcheck.sh" >> /etc/cron.d/ollama-healthcheck
El watchdog es violento a propósito: un LXC de Ollama que se cuelga no es un LXC con estado delicado, es un motor de inferencia que se puede reiniciar limpio. Si tienes conversación activa en Open WebUI, SQLite preserva el historial y LiteLLM reintenta la conexión. El reinicio limpio es preferible a dejar el LXC medio vivo.
Hardening del endpoint: cuando el agente deja tu LAN
El setup original de la guía anterior dejaba Ollama expuesto vía Tailscale o vía Caddy reverse proxy con TLS. Ambos son razonables, pero ambos asumen una premisa: que el endpoint Ollama solo es accesible por personas autorizadas. La realidad operativa es más fea: tarde o temprano alguien va a querer conectar un script desde una máquina que no tiene Tailscale, o vas a exponer el puerto vía Cloudflare Tunnel para “probar algo”, y la superficie de ataque se multiplica. El Ollama 0.40 ya soporta autenticación básica nativa; configurarla toma cinco minutos y elimina la mayor parte del riesgo.
OLLAMA_AUTH: autenticación básica en el endpoint
Desde la versión 0.32.5, Ollama entiende una variable OLLAMA_AUTH que activa autenticación contra un archivo de usuarios htpasswd. El archivo se lee en cada request, así que rotar usuarios no requiere reiniciar el daemon. El proceso es directo:
# Crear el archivo de usuarios (idealmente como root, fuera de /tmp)
apt-get install -y apache2-utils
htpasswd -cB /etc/ollama/.ollama-users admin
# Te pide la contraseña. La -B fuerza bcrypt.
htpasswd -B /etc/ollama/.ollama-users agente-codigo
htpasswd -B /etc/ollama/.ollama-users agente-investigacion
# Configurar Ollama
systemctl edit ollama
# Pegar:
# [Service]
# Environment="OLLAMA_AUTH=1"
sudo systemctl daemon-reload
sudo systemctl restart ollama
# Verificar que el endpoint pide autenticación
curl -i http://localhost:11434/api/tags
# Esperado: HTTP/1.1 401 Unauthorized
curl -u admin:tu-password http://localhost:11434/api/tags | jq '.models | length'
# Esperado: el número real de modelos
Si tu cliente no soporta Authorization: Basic directamente (algunos SDKs viejos no), LiteLLM como proxy abstrae la autenticación: la comunicacion interna LiteLLM↔Ollama usa las credenciales que configures en el panel de LiteLLM, y el cliente externo solo necesita la API key de LiteLLM, que rota más fácilmente y la puedes emitir por usuario o por agente.
Limitar CORS cuando hay un WebUI expuesto
Si el Open WebUI corre en otro host (no en el mismo LXC que Ollama), el browser tiene que poder hacer CORS a Ollama. La variable OLLAMA_ORIGINS define qué orígenes son válidos. Por defecto acepta localhost, 127.0.0.1 y 0.0.0.0, lo que para una LAN con Tailscale suele estar bien. Si necesitas abrir el WebUI en una URL pública (no recomendado), define orígenes explícitos en lugar de *:
systemctl edit ollama
# Pegar bajo [Service]:
# Environment="OLLAMA_ORIGINS=https://ia.example.lan,https://chat.example.lan"
systemctl daemon-reload
systemctl restart ollama
El orden de los origins no importa, pero la coma sí: si te falta una coma, Ollama lee toda la string como un solo origin y rechaza todo. El https:// es relevante — si tu WebUI está en http:// interno con certificados auto-firmados, agregalo igual a la lista para que el browser lo acepte.
Firewall a nivel host con nftables
El LXC de Ollama tiene su IP en la LAN. La política por defecto debería ser: solo aceptar conexiones en 11434 desde las IPs del LXC de Open WebUI, del LXC de LiteLLM, y de Tailscale. Todo lo demás, DROP silencioso. nftables en Debian 13 tiene sintaxis moderna y mejor rendimiento que iptables legacy.
# Dentro del LXC de Ollama, como root
apt-get install -y nftables
cat > /etc/nftables.d/ollama-rules.conf <<'EOF'
#!/usr/sbin/nft -f
table inet filter {
chain input {
type filter hook input priority 0; policy drop;
ct state established,related accept
iif lo accept
ip saddr 192.168.5.51 tcp dport 11434 accept # Open WebUI
ip saddr 192.168.5.52 tcp dport 11434 accept # LiteLLM
ip saddr 100.64.0.0/10 tcp dport 11434 accept # Tailscale CGNAT range
tcp dport { 22 } accept # SSH administrativo
counter drop
}
}
EOF
nft -f /etc/nftables.d/ollama-rules.conf
nft list ruleset > /etc/nftables.conf
systemctl enable --now nftables
El rango 100.64.0.0/10 es el espacio de CGNAT que Tailscale usa para sus IPs virtuales. Si tienes Tailscale en el homelab, todo tu tráfico cifrado va por ahí. Restringuir a esa subred evita que un dispositivo en la LAN de invitados (que está en 192.168.5.0/24) pueda tocar Ollama por accidente. Si no usas Tailscale, elimina esa línea y reemplaza por la subred del WebUI directamente.
Vale la pena operar un homelab IA 24/7?
Después de un año operando este setup con picos de tres agentes pidiendo inferencia en paralelo y valles donde nadie lo usa por seis horas, la respuesta corta es: sí, pero no para todos. El desglose de cuándo conviene y cuándo no, basado en lo que aprendí operando:
Vale la pena si: tu equipo usa agentes IA más de dos horas al día en total, tienes presupuesto para una GPU usada de 600-1000 USD y un NAS o PBS en una máquina separada (no en el mismo host), y valoras privacidad real sobre conveniencia inmediata. El punto de equilibrio económico está entre 4-6 meses para la mayoría de equipos chicos. Una RTX 3090 usada a 600 USD se amortiza contra 25-40 USD/mes de suscripción de Anthropic o OpenAI por usuario activo, y a partir del segundo usuario el ROI se acelera.
No vale la pena si: tu uso personal es menos de treinta minutos al día, no tienes un segundo host para correr PBS (corres backups contra el mismo host que respaldas, lo que es backup theater), o te importa más tener los últimos modelos release-week que ahorrar dinero. En ese caso, mejor pagar suscripción y dejar el dinero del SSD extra y la GPU en otras prioridades.
La zona gris que nadie te dice: cuando el homelab se rompe un domingo a las 3 AM y tu agente principal está caído hasta que lo arregles. Si tu trabajo depende 100% del agente, esa dependencia se convierte en deuda operacional. Tener un fallback a un proveedor comercial bien configurado en LiteLLM (con un budget mensual de 10 USD y rate limit de 1 req/s para emergencias) es la diferencia entre “el domingo se arruinó” y “el domingo fue un fastidio pero pude seguir”. Yo lo configuré seis meses tarde de lo que debería.
Preguntas frecuentes
¿Cuánto tiempo lleva migrar de mi setup actual a este stack operacional?
Si ya tienes el setup del post anterior funcionando (Proxmox + Ollama + WebUI), la primera sesión es de 2-3 horas para PBS + restic + primer snapshot completo. Las siguientes corridas (verificación, watchdog, alertas) van en bloques de 30-60 minutos durante una semana. El restore-test automatizado se programa una vez y corre solo cada trimestre.
¿Necesito un segundo servidor para correr Proxmox Backup Server?
Sí, es una regla dura de PBS: el destino no puede ser el mismo host que estás respaldando. Un mini-PC refurbished de 8 GB RAM con un SSD de 1 TB dedicado cuesta 150-250 USD usado y es suficiente. Si no quieres invertir, la alternativa es S3-compatible como Wasabi o Cloudflare R2 vía PBS 4.2, que salió del preview en abril 2026 y está production-ready.
¿Vale la pena configurar HA si tengo un solo nodo Proxmox?
No en el sentido clásico de HA con cluster corosync, pero sí el watchdog local que reinicia el LXC si Ollama deja de responder. El watchdog cuesta 10 líneas de bash en cron y resuelve los fallos más comunes (OOM, deadlock, modelo en mal estado) sin necesidad de invertir en un segundo nodo. Para protección real de hardware necesitas dos nodos, lo que duplica el costo.
¿Qué hago si restic reporta “tree node not found” durante una verificación?
Esto pasa cuando una subida se interrumpe a mitad (red caída, PBS reiniciado). Es recuperable: restic repair --repair-index --repair-packs reconstruye los índices desde los packs reales. Después corre restic check --read-data-subset=20% para confirmar la integridad. Si el error persiste en el mismo archivo, ese snapshot específico puede estar corrupto y la única opción es restic forget <snapshot-id> --prune. Por eso la verificación semanal detecta esto antes de que se acumule.
¿Cómo sé cuándo un modelo está mal cuantizado o no es compatible con mi GPU?
Los síntomas típicos son: Ollama carga el modelo a 100% pero la primera inferencia tarda más de 60 segundos, nvidia-smi muestra GPU utilization > 0% pero tokens/segundo reportado por ollama-exporter es menor a 5 t/s para un modelo 7B, y los logs tienen “CUDA error: out of memory” recurrentes. La causa suele ser un modelo declarado como Q4_K_M que en realidad baja como Q8_0, o un modelo con contexto largo que excede VRAM disponible. Verifica con ollama show <modelo> --modelfile y comparalo con el cuantizado en Hugging Face.
Lee también
- Creando tu Home Lab para Agentes IA: Ollama + Proxmox en 2026 — la guía de setup inicial sobre la que esta guía construye.
- PCI passthrough en Proxmox: pasar una GPU o dispositivo PCIe a una VM — el detalle fino de IOMMU, grupos, troubleshooting por familia de GPU.
- Backups, snapshots y restauración en Proxmox sin perder datos — el ABC de Proxmox Backup Server, retención, restauración granular.
- DevOps para Sysadmins: Configura Grafana y Prometheus en tu Homelab — los dashboards y las reglas de alertas que asumimos ya instaladas.
- Creando tu Home Server con Proxmox: Guía Completa para Sysadmins Linux — la base si estás arrancando de cero y quieres entender Proxmox en general antes del stack IA.
