Monitorización de Ollama en producción: logs, Prometheus y alertas

Tres diecisiete de la madrugada. Alerta roja en Slack: Ollama API timeout - service unavailable. Sistema de soporte con cientos de llamadas diarias. Solo logs básicos, sin monitorización. ¿VRAM llena? ¿Proceso muerto? ¿Red? A ciegas hasta las seis.
La falta de monitorización es una causa principal.
Aquí va el plan completo: logs → Prometheus + Grafana → AlertManager. Configs listas para copiar. ~30 minutos para un stack de producción.
Retos de monitorización en producción
Ollama no es un web service normal: consume muchos recursos. Cada modelo: 4-16 GB RAM (Markaicode). Cold start 10-30 s —si reinicia, el usuario espera media minuta.
Trampas habituales:
Fugas de memoria y VRAM agotada. Tras días, a veces no libera VRAM. Vi 24 GB con solo 2 GB libres; rechazos hasta quejas.
Cola de peticiones. Inferencia 5-20 s; decenas concurrentes → timeout. Sin métricas, solo intuición.
Latencia de carga de modelos. Cambio entre modelos = caja negra.
Objetivos claros: disponibilidad, rendimiento, recursos (VRAM), errores.
Opciones: Prometheus + Grafana (equipos pequeños); Langfuse (Prompt/respuesta); SigNoz (OpenTelemetry enterprise). Foco en Prometheus por ser la base más universal.
Logs y servicio systemd
Configuración systemd
# /etc/systemd/system/ollama.service
[Unit]
Description=Ollama Service
After=network.target
[Service]
Type=simple
User=ollama
Group=ollama
WorkingDirectory=/usr/share/ollama
Environment="OLLAMA_HOST=0.0.0.0:11434"
Environment="OLLAMA_MODELS=/data/ollama/models"
Environment="OLLAMA_DEBUG=1"
Environment="OLLAMA_LOG_FORMAT=json"
LimitNOFILE=65535
LimitNPROC=4096
MemoryMax=32G
Restart=always
RestartSec=10
ExecStart=/usr/local/bin/ollama serve
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
Restart=always+RestartSec=10: evita bucle de reinicios sin pausaMemoryMax=32G: protege otros servicios en la máquinaOLLAMA_DEBUG=1y JSON: depuración y parsing
sudo systemctl daemon-reload
sudo systemctl restart ollama
sudo systemctl enable ollama
Docker logging
services:
ollama:
image: ollama/ollama:latest
restart: always
ports:
- "11434:11434"
volumes:
- ./ollama_data:/root/.ollama
environment:
- OLLAMA_HOST=0.0.0.0:11434
- OLLAMA_DEBUG=1
deploy:
resources:
limits:
memory: 32G
logging:
driver: "json-file"
options:
max-size: "100m"
max-file: "5"
Máx. ~500 MB de logs.
Niveles de log
Variables de entorno de Ollama:
| Variable | Descripción | Producción |
|---|---|---|
OLLAMA_DEBUG | 1 = log detallado | Recomendado activar |
OLLAMA_LOG_LEVEL | INFO / DEBUG / WARN | INFO o DEBUG |
OLLAMA_LOG_FORMAT | text / json | JSON |
Suele valer la pena DEBUG: el disco aguanta y ahorra horas al depurar.
journalctl
# Tiempo real
sudo journalctl -u ollama -f
# Últimas 100 líneas
sudo journalctl -u ollama -n 100
# Hoy
sudo journalctl -u ollama --since today
# Errores
sudo journalctl -u ollama | grep -i "error"
# Exportar a archivo
sudo journalctl -u ollama --since "2026-04-12 00:00:00" > ollama-debug.log
Con JSON: sudo journalctl -u ollama -o json | jq 'select(.level=="error")' — solo errores, sin filtrar INFO a mano.
Prometheus + Grafana
ollama-exporter
services:
ollama-exporter:
image: frazco/ollama-exporter:latest
restart: always
ports:
- "9101:9101"
environment:
- OLLAMA_HOST=ollama:11434
depends_on:
- ollama
# prometheus.yml
global:
scrape_interval: 30s
evaluation_interval: 30s
scrape_configs:
- job_name: 'ollama-exporter'
static_configs:
- targets: ['ollama-exporter:9101']
labels:
instance: 'ollama-prod'
- job_name: 'nvidia-gpu'
static_configs:
- targets: ['localhost:9835']
Añade Prometheus al docker-compose:
services:
prometheus:
image: prom/prometheus:latest
container_name: prometheus
restart: always
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus_data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
volumes:
prometheus_data:
Métricas clave
| Métrica | Uso |
|---|---|
ollama_requests_total | Tasa de peticiones |
ollama_requests_failed | Errores |
ollama_model_load_duration_seconds | Cold start |
ollama_request_duration_seconds | P95/P99 |
ollama_tokens_per_second | Throughput |
También conviene métricas de sistema (node-exporter): node_cpu_seconds_total, node_memory_MemAvailable_bytes, node_network_receive_bytes_total.
GPU (nvidia exporter): nvidia_gpu_utilization, nvidia_gpu_memory_used_bytes, temperatura. Multi-GPU: etiqueta gpu_id.
docker run -d --name nvidia-exporter --restart always -p 9835:9835 --gpus all nvidia/gpu-prometheus-exporter:latest
Dashboard Grafana
Guarda este JSON e impórtalo en Grafana (Import Dashboard):
{
"dashboard": {
"title": "Ollama Production Monitor",
"panels": [
{
"title": "Request Rate",
"type": "graph",
"targets": [
{
"expr": "rate(ollama_requests_total[5m])",
"legendFormat": "Requests/sec"
}
],
"gridPos": {"x": 0, "y": 0, "w": 12, "h": 6}
},
{
"title": "Error Rate",
"type": "gauge",
"targets": [
{
"expr": "rate(ollama_requests_failed[5m]) / rate(ollama_requests_total[5m]) * 100",
"legendFormat": "Error %"
}
],
"gridPos": {"x": 12, "y": 0, "w": 6, "h": 6}
},
{
"title": "GPU Memory Usage",
"type": "graph",
"targets": [
{
"expr": "nvidia_gpu_memory_used_bytes / nvidia_gpu_memory_total_bytes * 100",
"legendFormat": "GPU {{gpu_id}}"
}
],
"gridPos": {"x": 0, "y": 6, "w": 12, "h": 6}
},
{
"title": "Response Latency P95",
"type": "stat",
"targets": [
{
"expr": "histogram_quantile(0.95, rate(ollama_request_duration_seconds_bucket[5m]))",
"legendFormat": "P95 Latency"
}
],
"gridPos": {"x": 12, "y": 6, "w": 6, "h": 6}
}
]
},
"overwrite": true
}
Arriba a la izquierda: tasa de peticiones; arriba a la derecha: gauge de errores (rojo >5%); abajo: VRAM por GPU y P95. Añade un panel tokens/s para comparar modelos.
Datasource: Configuration → Data Sources → Prometheus, URL http://prometheus:9090 (en docker-compose los contenedores se hablan por nombre).
Alertas y AlertManager
Tres niveles (aprendido a golpes):
| Nivel | Condición | Respuesta |
|---|---|---|
| Critical | Caída, VRAM >95%, error >20% | Inmediato (Slack + móvil) |
| Warning | Latencia >60 s, VRAM >80%, error >5% | 1 h (Slack) |
| Info | Cambio de modelo, deploy | Solo registro |
En prometheus.yml añade rule_files: ['ollama_alerts.yml'] y crea el archivo:
# ollama_alerts.yml
groups:
- name: ollama_critical
rules:
- alert: OllamaServiceDown
expr: up{job="ollama-exporter"} == 0
for: 1m
labels:
severity: critical
annotations:
summary: "Ollama Service Down"
description: "Exporter inalcanzable, el servicio puede haber caído"
- alert: GPUMemoryCritical
expr: nvidia_gpu_memory_used_bytes / nvidia_gpu_memory_total_bytes > 0.95
for: 2m
labels:
severity: critical
annotations:
summary: "VRAM casi agotada"
description: "GPU {{ gpu_id }} supera 95%, actualmente {{ $value | humanizePercentage }}"
- alert: HighErrorRate
expr: rate(ollama_requests_failed[5m]) / rate(ollama_requests_total[5m]) > 0.20
for: 3m
labels:
severity: critical
annotations:
summary: "Tasa de error demasiado alta"
description: "Error >20% en los últimos 5 minutos, revisa logs"
- name: ollama_warning
rules:
- alert: SlowResponseTime
expr: histogram_quantile(0.95, rate(ollama_request_duration_seconds_bucket[5m])) > 60
for: 5m
labels:
severity: warning
annotations:
summary: "P95 demasiado lento"
description: "El 95% de peticiones supera 60 segundos"
- alert: GPUMemoryWarning
expr: nvidia_gpu_memory_used_bytes / nvidia_gpu_memory_total_bytes > 0.80
for: 5m
labels:
severity: warning
annotations:
summary: "Uso alto de VRAM"
description: "GPU {{ gpu_id }} supera 80%"
- alert: ErrorRateWarning
expr: rate(ollama_requests_failed[5m]) / rate(ollama_requests_total[5m]) > 0.05
for: 5m
labels:
severity: warning
annotations:
summary: "Tasa de error en alza"
description: "Error >5% en los últimos 5 minutos"
for: Xm evita falsos positivos por picos. Umbral VRAM 95%: en la práctica, pasado ese punto falla casi al instante.
AlertManager (alertmanager.yml):
global:
resolve_timeout: 5m
route:
group_by: ['severity', 'alertname']
group_wait: 30s
group_interval: 5m
repeat_interval: 3h
routes:
- match:
severity: critical
receiver: 'critical-alerts'
- match:
severity: warning
receiver: 'warning-alerts'
receivers:
- name: 'critical-alerts'
slack_configs:
- api_url: 'https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK'
channel: '#ollama-critical'
send_resolved: true
- name: 'warning-alerts'
slack_configs:
- api_url: 'https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK'
channel: '#ollama-monitor'
send_resolved: true
Silences para mantenimiento: UI en :9093 o API POST a /api/v1/silences.
Herramientas LLM avanzadas
Langfuse
Trazas de Prompt, versiones, coste token, feedback de calidad. Ejemplo Python con Ollama:
from langfuse import Langfuse
import requests
langfuse = Langfuse(
public_key="pk-xxx",
secret_key="sk-xxx",
host="https://cloud.langfuse.com"
)
trace = langfuse.trace(
name="ollama-chat",
input={"prompt": user_prompt},
metadata={"model": "llama3.1"}
)
response = requests.post(
"http://localhost:11434/api/generate",
json={"model": "llama3.1", "prompt": user_prompt}
)
trace.update(
output=response.json()["response"],
metadata={"tokens": response.json().get("eval_count", 0)}
)
Self-hosted con Docker; LangChain tiene callback handler oficial.
SigNoz
OpenTelemetry unificado (logs + métricas + traces). Útil multi-servicio; requiere ~4 GB RAM.
| Escenario | Recomendación |
|---|---|
| Equipo <5 | Prometheus + Grafana |
| Trazas Prompt | + Langfuse |
| Empresa multi-servicio | SigNoz |
| Cloud | Servicio gestionado |
Yo uso Prometheus + Grafana + Langfuse: infra vs capa LLM separadas.
Cierre
No esperes al incidente de las 3 AM.
Con este stack llevo más de un año; alertas Warning de VRAM resueltas antes de Critical.
Pasos:
- Prometheus + Grafana básico
- Observa 3-5 días el rango normal
- Ajusta umbrales
- Añade Langfuse si necesitas Prompt
La monitorización se paga una vez y rinde siempre.
Repositorio de configs: github.com/yourname/ollama-monitoring-config (ejemplo —sustituir en despliegue real)
Serie:
- Guía completa de despliegue local Ollama
- Optimización de rendimiento Ollama —próximo artículo
FAQ
¿Qué métricas core en producción con Ollama?
¿Prometheus + Grafana vs Langfuse?
¿Umbrales de alerta razonables?
¿Logs Docker que crecen sin límite?
¿Monitorizar cada GPU en multi-GPU?
¿Diagnóstico rápido a las 3 AM?
6 min de lectura · Publicado el: 12 abr 2026 · Actualizado el: 21 ago 2026
Guía de Ollama local LLM
Si llegaste desde búsqueda, lo más rápido es ir al artículo anterior o siguiente de esta misma serie.
Anterior
Cuantización de modelos en Ollama: formato GGUF y pérdida de precisión
Principios de cuantización GGUF en Ollama, datos de evaluación Red Hat 500K+ sobre pérdida de precisión y recomendaciones por hardware para ejecutar LLM grandes en GPU de consumo.
Parte 16 de 18
Siguiente
Mnemo con Ollama: memoria local, despliegue y control
Mnemo da memoria entre sesiones a Ollama con Rust, SQLite y un grafo. Prueba su API y evalúa borrado, migración y límites al compartir entre agentes.
Parte 18 de 18



Comentarios
Inicia sesión con GitHub para dejar un comentario