Servidores

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 capability SYS_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

Alfonso Moreno | Sysadmin Senior

Soy Alfonso, sysadmin senior con RHCE y más de 10 años administrando sistemas Linux para empresas y proyectos personales. Me especializo en infraestructura, redes, virtualización, monitoring, automation y self-hosting. Sobre SoyLinux.com SoyLinux.com es un blog técnico en español sobre Linux, software libre y self-hosting. Lo mantengo en mi tiempo libre. Áreas de expertise - Linux server administration: RHEL, CentOS, Rocky, Alma, Fedora, Debian, Ubuntu - Network services: Postfix, BIND, Samba, NFS, Squid - Virtualización y containers: KVM, Proxmox, Docker, Podman - Monitoring: Prometheus, Grafana, Zabbix - Automation: Ansible, Bash scripting - Self-hosting: Vaultwarden, Nextcloud, Jellyfin, Immich, Homelab Contacto - Email: [email protected]