Monitoreo de servidores Linux con Grafana y Prometheus
Monitoreo de servidores Linux: Grafana + Prometheus desde cero
Si tienes un servidor (Nextcloud, Pi-hole, una app, lo que sea), necesitas saber qué está pasando dentro. CPU, RAM, disco, red, servicios caídos. Grafana + Prometheus es el estándar de la industria para esto, y montarlo es más fácil de lo que parece.
—
TL;DR — Lo que vas a tener al final
- Tiempo de setup: 1.5-2 horas para stack funcional.
- Requisitos: Server con 2+ GB RAM, Docker, root.
- Resultado: Stack Prometheus + Grafana + Alertmanager con alertas a Telegram.
- Troubleshooting: promtool query, métricas de exporters, log de alertmanager.
¿Qué es cada cosa?
Prometheus: base de datos de series temporales que recopila métricas de tus servidores cada N segundos. Las guarda y las expone para consultas.
Grafana: panel visual que consulta Prometheus y muestra gráficos bonitos. Soporta alertas, dashboards compartibles, múltiples data sources.
Node Exporter: agente que instalas en cada servidor a monitorear. Lee /proc, /sys y métricas del sistema, y las expone en formato Prometheus.
Alertmanager: recibe alertas de Prometheus y las enruta (email, Slack, Telegram, etc.).
El flujo: Node Exporter → Prometheus → Grafana → tu navegador.
—
Lo que necesitas
- Un servidor Linux (donde correrá Prometheus + Grafana).
- Uno o más servidores a monitorear (puede ser el mismo).
- Docker + Docker Compose instalados.
- 10-30 minutos.
—
Paso 1: Crear el directorio del stack
Vamos a centralizar todo el stack (configs de Prometheus, configs de Alertmanager, datos persistentes de Grafana) en un solo directorio bajo tu home. Así tienes un solo lugar para hacer backup, restaurar o migrar a otro servidor. Las subcarpetas prometheus/ y grafana/ son convenciones — Prometheus las monta con bind-mounts en el compose, y los volúmenes nombrados (prometheus_data, grafana_data) persisten incluso si borrás este directorio.
mkdir -p ~/monitoring && cd ~/monitoring
mkdir -p prometheus grafana
Si en algún momento necesitas mover el stack a otro server, basta copiar ~/monitoring/ y volver a correr docker compose up -d — los volúmenes nombrados se exportan con docker volume inspect y se migran aparte. Si quieres evitar este paso extra desde el principio, definí los volúmenes con paths absolutos en el docker-compose.yml (./prometheus-data:/prometheus).
—
Paso 2: Configurar Prometheus
Crea prometheus/prometheus.yml:
global:
scrape_interval: 15s
evaluation_interval: 15s
# Configuración de Alertmanager
alerting:
alertmanagers:
- static_configs:
- targets: ['alertmanager:9093']
# Reglas de alerta
rule_files:
- "alerts.yml"
# Qué métricas scrapeamos
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
- job_name: 'node'
static_configs:
- targets: ['node-exporter:9100']
# Si tienes otros servidores en la LAN, agregas aquí:
# - job_name: 'node-servidor-2'
# static_configs:
# - targets: ['192.168.1.50:9100']
Crea prometheus/alerts.yml:
groups:
- name: sistema
rules:
# Disco casi lleno
- alert: DiscoCasiLleno
expr: (node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"}) * 100 < 10
for: 5m
labels:
severity: warning
annotations:
summary: "Disco casi lleno en {{ $labels.instance }}"
description: "Menos del 10% libre en /"
# CPU alta
- alert: CPUAlta
expr: 100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 80
for: 5m
labels:
severity: warning
annotations:
summary: "CPU > 80% en {{ $labels.instance }}"
# Memoria alta
- alert: MemoriaAlta
expr: (1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100 > 90
for: 5m
labels:
severity: critical
annotations:
summary: "Memoria > 90% en {{ $labels.instance }}"
# Servidor caído (no responde)
- alert: ServidorCaido
expr: up == 0
for: 2m
labels:
severity: critical
annotations:
summary: "{{ $labels.instance }} no responde"
—
Paso 3: Configurar Alertmanager
Crea alertmanager/alertmanager.yml:
global:
smtp_smarthost: 'smtp.gmail.com:587'
smtp_from: '[email protected]'
smtp_auth_username: '[email protected]'
smtp_auth_password: 'tu-app-password-de-gmail'
route:
receiver: 'email-default'
group_wait: 30s
group_interval: 5m
repeat_interval: 4h
receivers:
- name: 'email-default'
email_configs:
- to: '[email protected]'
send_resolved: true
Para Telegram, agrega otra ruta:
receivers:
- name: 'telegram'
telegram_configs:
- bot_token: 'TU_BOT_TOKEN'
chat_id: TU_CHAT_ID
send_resolved: true
route:
receiver: 'telegram'
routes:
- match:
severity: critical
receiver: 'email-default'
—
Paso 4: Crear el docker-compose.yml
services:
prometheus:
image: prom/prometheus:v2.54.1 # Pinneado: latest se rompe entre versiones; v2.54.1 es estable 2024
container_name: prometheus
restart: always
ports:
- "9090:9090"
volumes:
- ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
- ./prometheus/alerts.yml:/etc/prometheus/alerts.yml:ro
- prometheus_data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.retention.time=30d'
- '--storage.tsdb.path=/prometheus'
alertmanager:
image: prom/alertmanager:v0.27.0 # Pinneado
container_name: alertmanager
restart: always
ports:
- "9093:9093"
volumes:
- ./alertmanager/alertmanager.yml:/etc/alertmanager/alertmanager.yml:ro
node-exporter:
image: prom/node-exporter:v1.8.2 # Pinneado
container_name: node-exporter
restart: always
ports:
- "9100:9100"
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/rootfs:ro
command:
- '--path.procfs=/host/proc'
- '--path.sysfs=/host/sys'
- '--path.rootfs=/rootfs'
- '--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)"
- /:/rootfs:ro
command:
- '--path.procfs=/host/proc'
- '--path.sysfs=/host/sys'
- '--path.rootfs=/rootfs'
- '--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)'
grafana:
image: grafana/grafana:latest
container_name: grafana
restart: always
ports:
- "3000:3000"
environment:
GF_SECURITY_ADMIN_USER: admin
GF_SECURITY_ADMIN_PASSWORD: cambia-esto-ahora
GF_USERS_ALLOW_SIGN_UP: "false"
volumes:
- grafana_data:/var/lib/grafana
volumes:
prometheus_data:
grafana_data:
Arranca todo:
docker compose up -d (con guion y espacio, no docker-compose) lee el docker-compose.yml del directorio actual, descarga las imágenes que faltan, crea la red interna monitoring_default y arranca los cuatro servicios en background (-d). El down se hace con docker compose down (conserva volúmenes) o docker compose down -v (borra volúmenes — peligroso en producción).
docker compose up -d
docker compose ps
Espera 30 segundos y verifica:
/-/healthy es el endpoint de health check que Prometheus expone en su puerto 9090; devuelve 200 OK cuando el servidor está listo para servir queries. /api/health es el equivalente de Grafana, devuelve {"database": "ok"} cuando SQLite y plugins están inicializados. Si curl no devuelve 200 después de 30 segundos, revisa docker compose logs prometheus y docker compose logs grafana para encontrar el error.
# Prometheus debe responder en 9090
curl http://localhost:9090/-/healthy
# Grafana debe responder en 3000
curl http://localhost:3000/api/health
Troubleshooting: si curl da Connection refused, el puerto no está publicado al host o el contenedor está en crash loop. verifica con docker compose ps que todos los servicios están Up (healthy) (no Up ni Restarting). Si Prometheus queda en Restarting, mira docker compose logs prometheus — causas típicas: error de sintaxis en prometheus.yml (YAML es estricto con indentación), o el volumen prometheus_data quedó corrupto de un run anterior (borrá el volumen con docker compose down -v y arrancá de nuevo). Si Grafana arranca pero el healthcheck falla, suele ser porque no terminó de inicializar SQLite — espera 60 segundos más.
—
Paso 5: Configurar Grafana
Abre http://tu-servidor:3000 en el navegador. Login con admin / cambia-esto-ahora (te pedirá cambiar la contraseña).
Agregar Prometheus como data source
1. Click en el ícono de engranaje (esquina inferior izquierda) → Data Sources.
2. Click Add data source.
3. Selecciona Prometheus.
4. URL: http://prometheus:9090 (nombre del servicio Docker).
5. Click Save & Test. Debe decir “Data source is working”.
Importar dashboard de Node Exporter
La comunidad mantiene dashboards oficiales. El más usado es el ID 1860 (Node Exporter Full):
1. Click en el ícono “+” → Import dashboard.
2. Pega el ID 1860.
3. Selecciona tu data source de Prometheus.
4. Click Import.
Tendrás un dashboard con 50+ paneles: CPU, RAM, disco, red, load average, file descriptors, etc.
—
Paso 6: Configurar alertas desde Grafana (opcional, más amigable)
Grafana puede gestionar alertas sin tocar Prometheus. Ve a Alerting → Alert rules y crea:
# Ejemplo: alerta de CPU alta
Query: 100 - (avg by(host) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)
Condition: > 80
For: 5m
Notification: email-admin
Más visual que editar YAML. Pero las alertas en Prometheus (las del alerts.yml) son más potentes para reglas complejas.
—
Paso 7: Monitorear múltiples servidores
Si quieres monitorear otros servidores además del principal, instala Node Exporter en cada uno:
prometheus-node-exporter es el paquete Debian/Ubuntu oficial del exporter de Prometheus (binario escrito en Go que expone métricas del sistema en /metrics formato Prometheus). El servicio systemd escucha en 9100 y lee /proc, /sys y selectores de hardware sin necesidad de configuración adicional.
# En cada servidor Linux a monitorear
sudo apt install -y prometheus-node-exporter
sudo systemctl enable --now prometheus-node-exporter
Por defecto escucha en 9100. Verifica:
curl localhost:9100/metrics devuelve cientos de líneas en formato nombre{labels} valor, una por cada métrica expuesta (CPU, memoria, red, FS, etc.). Si no devuelve nada, el servicio no está corriendo (systemctl status prometheus-node-exporter) o el puerto está bloqueado por firewall local.
curl http://localhost:9100/metrics | head -5
# Debe devolver métricas en texto plano
Ahora edita prometheus/prometheus.yml y agrega el servidor:
scrape_configs:
- job_name: 'node-servidor-2'
static_configs:
- targets: ['192.168.1.50:9100']
labels:
host: servidor-web
- job_name: 'node-servidor-3'
static_configs:
- targets: ['192.168.1.51:9100']
labels:
host: servidor-db
Reinicia Prometheus:
docker compose restart prometheus envía SIGTERM al contenedor principal y lo vuelve a levantar; el healthcheck de Node Exporter tardará 15 segundos en reflejarse en el dashboard. Si no aparece, revisa curl http://prometheus:9090/api/v1/targets desde dentro del contenedor — state: up significa que el scrape funciona, state: down muestra el error (típicamente connection refused o i/o timeout).
docker compose restart prometheus
Importante: si los servidores están detrás de NAT o firewall, usa Pushgateway o un túnel WireGuard/Tailscale para conectar.
Troubleshooting: si después del restart Prometheus no ve el nuevo target, casi siempre es por DNS o firewall. Los nombres como node-servidor-2 no resuelven: usa la IP directa (192.168.1.50:9100). Si la IP es privada y el Prometheus está en otro segmento, el firewall del server destino (o iptables) está bloqueando el :9100. verifica con nc -zv 192.168.1.50 9100 desde el server de Prometheus. Para servidores detrás de NAT estricto sin IP pública, las opciones son: (a) Pushgateway (el server empuja métricas a un endpoint público), (b) tunnel inverso SSH (ssh -R 9100:localhost:9100), o (c) mesh VPN (Tailscale, WireGuard) que pone a todos los nodos en una red privada virtual.
—
Paso 8: Configurar notificaciones reales
Telegram (recomendado, gratis, instantáneo)
1. Habla con @BotFather en Telegram, crea un bot, obtén el token.
2. Envía un mensaje a tu bot, luego ve a https://api.telegram.org/botTU_TOKEN/getUpdates para obtener tu chat_id.
3. Edita alertmanager/alertmanager.yml:
receivers:
- name: 'telegram'
telegram_configs:
- bot_token: '123456:ABC-DEF...'
chat_id: 987654321
send_resolved: true
message: |
{{ .CommonAnnotations.summary }}
{{ .CommonAnnotations.description }}
4. Reinicia Alertmanager: docker compose restart alertmanager.
Discord / Slack / PagerDuty
Soportados nativamente. Cambia el receiver:
# Slack
slack_configs:
- api_url: 'https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXX'
channel: '#alertas'
# Discord (via webhook)
webhook_configs:
- url: 'https://discord.com/api/webhooks/...'
—
Dashboards útiles que vale la pena importar
| ID | Nombre | Para qué sirve |
|---|
| 1860 | Node Exporter Full | Métricas completas del sistema |
|---|
| 893 | Docker Container (cAdvisor) | Métricas de contenedores Docker |
|---|
| 315 | Prometheus Stats | Estado de Prometheus mismo |
|---|
| 6417 | Blackbox Exporter | Uptime de sitios web |
|---|
| 11074 | Nextcloud | Métricas específicas de Nextcloud |
|---|

Pingback: Cómo ver el ancho de banda en CentOS con iftop
Pingback: Guía completa para instalar htop y btop en Linux
Pingback: 10 comandos básicos de Linux que todo usuario debe saber
Pingback: Instalar Proxmox VE: guía y primera VM 2026
Pingback: LXC vs VM en Proxmox: cuál usar y cuándo
Pingback: Backups y snapshots en Proxmox: guía segura
Pingback: Proxmox + OPNsense: red completa para homelab
Pingback: Suricata en OPNsense: IDS/IPS sin perder velocidad