--- title: "Stack de logs con Grafana, Loki y Alloy en Ubuntu 24.04" description: "Monta un stack de logs en Ubuntu 24.04: Loki para guardarlos, Alloy para recoger y procesar los de Nginx y journald, y Grafana para paneles y alertas de logs." category: "Stack de Observabilidad" language: es source: https://cubepath.com/es/docs/stack-de-observabilidad/configuracion-completa-de-grafana-loki-y-promtail updated: 2026-09-25 --- # Stack de logs con Grafana, Loki y Alloy en Ubuntu 24.04 Un sistema de logs centralizado te permite buscar en los logs de todos tus servidores desde un único sitio, graficar tasas de error y recibir una alerta cuando algo falla. En este tutorial montarás uno en un único servidor Ubuntu 24.04 con tres componentes de Grafana Labs: **Loki** guarda los logs, **Grafana Alloy** los recoge y procesa, y **Grafana** es la interfaz para buscar, crear paneles y definir alertas. Procesarás los logs de acceso de Nginx, crearás un panel con los códigos de estado HTTP y una alerta que salta cuando se disparan los errores 5xx. Las versiones anteriores de este stack usaban Promtail como recolector. Promtail llegó al final de su vida útil en 2026 y Grafana Alloy es su sustituto, así que esta guía usa Alloy y explica cómo convertir una configuración de Promtail existente. ## Requisitos previos Para seguir esta guía necesitas: - Un servidor con Ubuntu 24.04 LTS y al menos 2 GB de RAM (con 4 GB irás más holgado), por ejemplo un VPS de CubePath. - Un usuario no root con privilegios `sudo` y UFW activado con SSH permitido. - Un dominio con un registro A que apunte a la IP pública del servidor. En esta guía lo llamamos `your_domain`; Grafana se servirá en `https://your_domain`. ## Paso 1: Añadir el repositorio APT de Grafana Loki, Alloy y Grafana se publican en el repositorio APT firmado de Grafana. Añade su clave y el repositorio: ```bash sudo apt update sudo apt install -y gpg wget curl jq sudo mkdir -p /etc/apt/keyrings wget -q -O - https://apt.grafana.com/gpg.key | gpg --dearmor | sudo tee /etc/apt/keyrings/grafana.gpg > /dev/null echo "deb [signed-by=/etc/apt/keyrings/grafana.gpg] https://apt.grafana.com stable main" | sudo tee /etc/apt/sources.list.d/grafana.list sudo apt update ``` Instala los tres paquetes, además de Nginx, que hará de proxy inverso para Grafana y de fuente de logs de ejemplo: ```bash sudo apt install -y loki alloy grafana nginx ``` ## Paso 2: Configurar Loki La configuración empaquetada de Loki guarda los datos en `/tmp`, que no sobrevive a un reinicio. Crea un directorio de datos persistente: ```bash sudo mkdir -p /var/lib/loki sudo chown loki:loki /var/lib/loki ``` Abre el archivo de configuración: ```bash sudo nano /etc/loki/config.yml ``` Sustituye su contenido por una configuración de un solo nodo que escucha solo en localhost, usa el índice TSDB con el esquema v13 y borra los logs a los 30 días: ```yaml auth_enabled: false server: http_listen_address: 127.0.0.1 http_listen_port: 3100 grpc_listen_address: 127.0.0.1 grpc_listen_port: 9096 common: instance_addr: 127.0.0.1 path_prefix: /var/lib/loki storage: filesystem: chunks_directory: /var/lib/loki/chunks rules_directory: /var/lib/loki/rules replication_factor: 1 ring: kvstore: store: inmemory schema_config: configs: - from: 2024-04-01 store: tsdb object_store: filesystem schema: v13 index: prefix: index_ period: 24h limits_config: retention_period: 720h compactor: working_directory: /var/lib/loki/compactor retention_enabled: true retention_delete_delay: 2h delete_request_store: filesystem analytics: reporting_enabled: false ``` Como Alloy y Grafana se ejecutan en el mismo servidor, Loki no necesita ser accesible desde fuera. La guía de instalación de Grafana Loki de esta misma sección explica cada bloque en detalle. Activa y reinicia Loki, y espera a que indique que está listo (unos 15 segundos): ```bash sudo systemctl enable loki sudo systemctl restart loki curl -s http://127.0.0.1:3100/ready ``` ```output ready ``` ## Paso 3: Recoger los logs con Grafana Alloy Alloy se configura con pipelines de componentes: las fuentes leen los logs, los procesadores los transforman y los escritores los envían a Loki. Crearás dos pipelines: uno para el journal de systemd y otro para los logs de acceso de Nginx. Alloy se ejecuta con el usuario `alloy`. Dale acceso de lectura al journal y a `/var/log/nginx`, cuyos archivos pertenecen al grupo `adm`: ```bash sudo usermod -aG systemd-journal,adm alloy ``` Abre la configuración de Alloy: ```bash sudo nano /etc/alloy/config.alloy ``` Sustituye su contenido por lo siguiente: ```alloy // Destino al que envían sus logs todos los pipelines. loki.write "local" { endpoint { url = "http://127.0.0.1:3100/loki/api/v1/push" } } // Pipeline 1: journal de systemd. loki.relabel "journal" { forward_to = [] rule { source_labels = ["__journal__systemd_unit"] target_label = "unit" } } loki.source.journal "system" { max_age = "12h" relabel_rules = loki.relabel.journal.rules labels = { job = "systemd-journal", host = constants.hostname } forward_to = [loki.write.local.receiver] } // Pipeline 2: log de acceso de Nginx. local.file_match "nginx" { path_targets = [{ "__path__" = "/var/log/nginx/access.log", "job" = "nginx", "host" = constants.hostname, }] } loki.source.file "nginx" { targets = local.file_match.nginx.targets forward_to = [loki.process.nginx.receiver] } loki.process "nginx" { // Descarta los health checks del balanceador antes de guardarlos. stage.drop { expression = `"GET /healthz ` drop_counter_reason = "healthcheck" } // Extrae campos del formato "combined" por defecto. stage.regex { expression = `^(?P\S+) \S+ \S+ \[[^\]]+\] "(?P\S+) (?P\S+) [^"]*" (?P\d{3}) ` } // Adjunta el código de estado como metadatos estructurados, no como etiqueta del índice. stage.structured_metadata { values = { status = "" } } forward_to = [loki.write.local.receiver] } ``` Cómo funciona el pipeline de Nginx: - `local.file_match` define qué archivos seguir y las etiquetas que recibe cada línea (`job` y `host`). `loki.source.file` los lee y recuerda su posición, así que los reinicios no duplican líneas. - `stage.drop` descarta cualquier línea que coincida con la expresión regular. Aquí son las peticiones de health check, que añaden volumen pero no información. - `stage.regex` analiza cada línea con grupos de captura con nombre y los guarda en un mapa temporal de campos extraídos. - `stage.structured_metadata` adjunta el `status` extraído a cada línea como metadatos. Se puede filtrar por él al consultar, pero no crea flujos nuevos. > Importante: Las etiquetas crean flujos en Loki, y cada flujo tiene un coste. Limita las etiquetas a unos pocos valores de baja cardinalidad (`job`, `host`, `unit`). No conviertas nunca en etiquetas rutas, IP de clientes ni identificadores de usuario; filtra por ellos al consultar. Comprueba la sintaxis del archivo. `alloy fmt` termina con un error que señala la línea si algo está mal: ```bash alloy fmt /etc/alloy/config.alloy > /dev/null && echo "sintaxis OK" ``` Activa y reinicia Alloy, y lee su log: ```bash sudo systemctl enable alloy sudo systemctl restart alloy sudo journalctl -u alloy -n 30 --no-pager ``` No debería haber líneas `level=error`. Genera algunas peticiones a Nginx y confirma que Loki tiene ambos jobs: ```bash for i in 1 2 3; do curl -s -o /dev/null http://localhost/; curl -s -o /dev/null http://localhost/missing; done curl -s http://127.0.0.1:3100/loki/api/v1/label/job/values | jq -c .data ``` ```output ["nginx","systemd-journal"] ``` ### Migrar una configuración de Promtail existente Si ya usas Promtail, Alloy puede traducir su configuración YAML al formato de Alloy: ```bash alloy convert --source-format=promtail --output=/tmp/promtail-converted.alloy /etc/promtail/config.yml ``` Revisa el resultado, intégralo en `/etc/alloy/config.alloy` y, cuando Alloy ya esté enviando logs, detén y desactiva Promtail con `sudo systemctl disable --now promtail` para que las líneas no se envíen dos veces. ## Paso 4: Configurar Grafana detrás de Nginx con HTTPS Por defecto Grafana escucha en el puerto 3000 en todas las interfaces. Limítalo a localhost e indícale su URL pública. Abre su archivo de configuración: ```bash sudo nano /etc/grafana/grafana.ini ``` En la sección `[server]`, define estas tres claves (quita el `;` inicial que las comenta): ```ini [server] http_addr = 127.0.0.1 domain = your_domain root_url = https://your_domain/ ``` Añade Loki como origen de datos mediante provisioning, para que exista en cuanto arranque Grafana y no se pueda modificar por error desde la interfaz: ```bash sudo nano /etc/grafana/provisioning/datasources/loki.yaml ``` ```yaml apiVersion: 1 datasources: - name: Loki type: loki access: proxy url: http://127.0.0.1:3100 isDefault: true editable: false ``` Activa e inicia Grafana: ```bash sudo systemctl enable --now grafana-server curl -s http://127.0.0.1:3000/api/health | jq -r .database ``` ```output ok ``` Ahora crea un bloque server de Nginx que haga de proxy hacia Grafana. Grafana Live usa WebSockets en `/api/live/`, que necesita las cabeceras `Upgrade`: ```bash sudo nano /etc/nginx/sites-available/grafana ``` ```nginx map $http_upgrade $connection_upgrade { default upgrade; '' close; } server { listen 80; listen [::]:80; server_name your_domain; location / { proxy_set_header Host $host; proxy_pass http://127.0.0.1:3000; } location /api/live/ { proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; proxy_set_header Host $host; proxy_pass http://127.0.0.1:3000; } } ``` Activa el sitio, comprueba la configuración y recarga Nginx: ```bash sudo ln -s /etc/nginx/sites-available/grafana /etc/nginx/sites-enabled/ sudo nginx -t sudo systemctl reload nginx ``` Abre HTTP y HTTPS en el cortafuegos y solicita un certificado de Let's Encrypt. Certbot edita el bloque server para añadir TLS y una redirección desde HTTP: ```bash sudo ufw allow 'Nginx Full' sudo apt install -y certbot python3-certbot-nginx sudo certbot --nginx -d your_domain ``` Visita `https://your_domain`. Inicia sesión con el usuario `admin` y la contraseña `admin`; Grafana te obliga a definir una contraseña nueva en ese momento. Elige una robusta. ## Paso 5: Explorar los logs en Grafana Abre **Explore** en el menú izquierdo. El origen de datos Loki ya está seleccionado. Cambia el editor de consultas al modo **Code** y ejecuta: ```logql {job="nginx"} ``` Verás las peticiones que hiciste en el paso 3, además del tráfico que estás generando ahora con Grafana. Algunas consultas útiles para probar: Solo errores de servidor y de cliente, usando los metadatos estructurados que extrajo Alloy: ```logql {job="nginx"} | status >= 400 ``` Analiza la línea al consultar con `pattern` para filtrar por campos que no extrajiste en Alloy, aquí la ruta de la petición: ```logql {job="nginx"} | pattern ` - <_> [<_>] " <_>" <_>` | path = "/missing" ``` Accesos SSH fallidos del journal: ```logql {job="systemd-journal", unit="ssh.service"} |= "Failed password" ``` ## Paso 6: Crear un panel Crea un panel (dashboard) con dos visualizaciones: la tasa de peticiones por código de estado y las líneas de error en bruto. 1. Ve a **Dashboards > New > New dashboard** y pulsa **Add visualization**. Selecciona el origen de datos **Loki**. 2. En modo Code, escribe la siguiente consulta y define la leyenda como `{{status}}`: ```logql sum by (status) (count_over_time({job="nginx"} [$__auto])) ``` 3. Mantén la visualización **Time series**, titula el panel `Peticiones Nginx por estado` y vuelve al dashboard. 4. Añade una segunda visualización, elige el tipo **Logs** y usa esta consulta: ```logql {job="nginx"} | status >= 500 ``` 5. Titúlala `Respuestas 5xx` y guarda el dashboard como `Nginx`. `count_over_time` convierte las líneas de log en un número por ventana de tiempo, y `sum by (status)` produce una serie por código de estado. `$__auto` deja que Grafana elija la ventana según el nivel de zoom. ## Paso 7: Alertas sobre patrones de log Grafana puede evaluar consultas de métricas LogQL de forma periódica y avisarte cuando superan un umbral. Primero crea un punto de contacto en **Alerting > Contact points**, por ejemplo correo (requiere configurar la sección `[smtp]` de `grafana.ini`) o un webhook de Slack. Usa **Test** para confirmar que llega. Después crea la regla en **Alerting > Alert rules > New alert rule**: 1. Llámala `Pico de 5xx en Nginx` y selecciona el origen de datos **Loki**. 2. Escribe esta consulta, que cuenta las respuestas 5xx de los últimos 5 minutos: ```logql sum(count_over_time({job="nginx"} | status >= 500 [5m])) ``` 3. Define la condición de umbral como **Is above 10**. 4. Crea una carpeta y un grupo de evaluación que se evalúe cada `1m`, y define un periodo pendiente (pending period) de `5m` para que un pico breve no despierte a nadie. 5. Selecciona tu punto de contacto y guarda la regla. Para probarla, baja temporalmente el umbral a 0 y pide una URL a la que tu aplicación responda con un error 5xx. La regla pasa a **Pending**, después a **Firing**, y recibes una notificación. Restaura el umbral al terminar. ## Solución de problemas **Los logs de Nginx no aparecen en Loki.** Revisa `sudo journalctl -u alloy | grep -i nginx`. Un error `permission denied` significa que el cambio al grupo `adm` no se ha aplicado; confírmalo con `id alloy` y reinicia Alloy. **Grafana muestra "Data source connected, but no labels found".** Loki funciona pero no ha recibido nada. Comprueba que Alloy está en marcha y que `curl -s http://127.0.0.1:3100/loki/api/v1/labels` lista etiquetas. **`502 Bad Gateway` en `https://your_domain`.** Grafana no está en marcha o no escucha en `127.0.0.1:3000`. Revisa `sudo systemctl status grafana-server` y `sudo ss -tlnp | grep 3000`. **El filtro `| status >= 400` no devuelve nada.** Los metadatos estructurados solo existen en las líneas recibidas después de añadir el pipeline. Las líneas anteriores se pueden filtrar con la consulta `pattern` del paso 5. Confirma también que el `log_format` de Nginx es el `combined` por defecto, o ajusta la expresión regular de `stage.regex`. **Loki registra `too many outstanding requests` desde Grafana.** Un dashboard está consultando un rango de tiempo muy amplio. Acota el rango o añade etiquetas más selectivas al selector de flujos. ## Conclusión Ya tienes un stack de logs completo en Ubuntu 24.04: Alloy recoge el journal y los logs de Nginx y procesa el log de acceso, Loki lo guarda todo durante 30 días y Grafana, servido por HTTPS, te da búsqueda, un panel y una alerta sobre errores 5xx. Para ampliarlo, instala Alloy en tus otros servidores y apunta su `loki.write` a esta instancia de Loki a través de un proxy inverso con TLS y autenticación, añade pipelines para los logs de tus aplicaciones (las etapas `stage.json` y `stage.logfmt` procesan formatos estructurados) y añade Prometheus como segundo origen de datos para correlacionar métricas y logs en los mismos paneles.