Grafana Loki es un sistema de agregación de logs que, a diferencia de Elasticsearch, solo indexa un pequeño conjunto de etiquetas (labels) y guarda el contenido de los logs comprimido, lo que lo hace barato de operar. Durante años el agente habitual para enviarle logs fue Promtail, pero Grafana Labs lo ha dado por finalizado (fin de vida el 2 de marzo de 2026) y su sustituto oficial es Grafana Alloy. En este tutorial instalarás Loki, Alloy y Grafana en Ubuntu 24.04 desde el repositorio oficial de Grafana, recogerás los logs del journal de systemd y de Nginx, los consultarás con LogQL y verás cómo convertir una configuración de Promtail existente.
Requisitos previos
- Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath, con al menos 2 vCPU y 2 GB de RAM.
- Un usuario no root con privilegios
sudo. - UFW activo, con SSH permitido.
- Opcional: Nginx instalado, para el ejemplo de logs de acceso.
En esta guía los tres componentes corren en el mismo servidor y se comunican por 127.0.0.1.
Paso 1: Añadir el repositorio de Grafana
Loki, Alloy y Grafana se publican en el mismo repositorio APT. Descarga su clave en /etc/apt/keyrings y añade el repositorio:
sudo apt update
sudo apt install -y gpg curl jq
curl -fsSL https://apt.grafana.com/gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/grafana.gpg
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
Comprueba que APT ve los paquetes:
apt-cache policy loki alloy grafana | grep -A1 '^[a-z]'
loki:
Installed: (none)
alloy:
Installed: (none)
grafana:
Installed: (none)
Paso 2: Instalar y configurar Loki
Instala Loki:
sudo apt install -y loki
El paquete crea el usuario loki, el servicio loki y un fichero /etc/loki/config.yml de ejemplo que guarda los datos en /tmp, donde se perderían al reiniciar. Crea un directorio de datos persistente:
sudo mkdir -p /var/lib/loki
sudo chown loki:loki /var/lib/loki
Guarda una copia del fichero original y ábrelo:
sudo cp /etc/loki/config.yml /etc/loki/config.yml.orig
sudo nano /etc/loki/config.yml
Sustituye su contenido por esta configuración para un solo nodo con almacenamiento en disco local y 31 días de retención:
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
log_level: info
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: 744h
compactor:
working_directory: /var/lib/loki/compactor
retention_enabled: true
delete_request_store: filesystem
Los puntos clave:
auth_enabled: falsedesactiva el modo multiinquilino. Por eso Loki escucha solo en127.0.0.1: cualquiera que llegue al puerto 3100 podría leer y escribir logs.schema_configusa el índicetsdbcon el esquemav13, los recomendados en Loki 3.retention_period(744 h = 31 días) solo se aplica si el compactor tieneretention_enabled: true, y Loki 3 exige indicardelete_request_storeen ese caso.
Reinicia Loki y comprueba que está listo. El endpoint /ready puede devolver 503 durante unos 15 segundos tras el arranque:
sudo systemctl restart loki
sudo systemctl enable loki
curl -s http://127.0.0.1:3100/ready
ready
Si no responde, revisa el log con sudo journalctl -u loki -n 50 --no-pager.
Paso 3: Instalar Grafana Alloy
Alloy es un colector de telemetría que se configura mediante componentes conectados entre sí: uno lee logs, otro los procesa y otro los envía a Loki. Instálalo:
sudo apt install -y alloy
El servicio se ejecuta con el usuario alloy. Para leer el journal de systemd y los ficheros de /var/log, como los de Nginx (propiedad del grupo adm), añádelo a los grupos correspondientes:
sudo usermod -aG adm,systemd-journal alloy
Paso 4: Recoger los logs del journal y de Nginx
Guarda una copia de la configuración de ejemplo y abre el fichero:
sudo cp /etc/alloy/config.alloy /etc/alloy/config.alloy.orig
sudo nano /etc/alloy/config.alloy
Sustituye su contenido por lo siguiente:
// Destino: Loki local
loki.write "local" {
endpoint {
url = "http://127.0.0.1:3100/loki/api/v1/push"
}
}
// Journal de systemd
loki.relabel "journal" {
forward_to = []
rule {
source_labels = ["__journal__systemd_unit"]
target_label = "unit"
}
rule {
source_labels = ["__journal_priority_keyword"]
target_label = "level"
}
}
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]
}
// Logs de Nginx
local.file_match "nginx" {
path_targets = [{
"__path__" = "/var/log/nginx/*.log",
"job" = "nginx",
"host" = constants.hostname,
}]
}
loki.source.file "nginx" {
targets = local.file_match.nginx.targets
forward_to = [loki.write.local.receiver]
}
Qué hace cada bloque:
loki.write "local"envía las entradas a la API de Loki.loki.source.journallee el journal desde las últimas 12 horas. Las reglas deloki.relabelconvierten los campos internos del journal en las etiquetasunit(por ejemplossh.service) ylevel(info,warning,err...).local.file_matchyloki.source.filesiguen los ficheros de Nginx. Alloy guarda la posición de lectura de cada fichero, así que no reenvía líneas tras un reinicio.
Fíjate en que todas las etiquetas tienen pocos valores posibles: job, host, unit y level. Loki crea un stream por cada combinación distinta de etiquetas, así que nunca uses como etiqueta valores como IPs, IDs de usuario o rutas; esos datos se extraen al consultar, como verás en el paso 6.
Si no tienes Nginx, elimina los dos últimos bloques. Comprueba la sintaxis y reinicia Alloy para que también aplique los nuevos grupos del usuario:
alloy fmt /etc/alloy/config.alloy > /dev/null && echo "sintaxis correcta"
sudo systemctl restart alloy
sudo systemctl enable alloy
sintaxis correcta
Revisa que no haya errores de permisos en el log de Alloy:
sudo journalctl -u alloy -n 20 --no-pager
Alloy también tiene una interfaz web de depuración en http://127.0.0.1:12345, que muestra el estado de cada componente. Para verla desde tu equipo, abre un túnel con ssh -L 12345:127.0.0.1:12345 your_user@your_server_ip.
Paso 5: Comprobar que Loki recibe los logs
Pide a Loki la lista de etiquetas que conoce:
curl -s http://127.0.0.1:3100/loki/api/v1/labels | jq
{
"status": "success",
"data": [
"host",
"job",
"level",
"service_name",
"unit"
]
}
service_name la añade Loki automáticamente a partir de otras etiquetas. Consulta ahora los valores de unit:
curl -s http://127.0.0.1:3100/loki/api/v1/label/unit/values | jq -r '.data[]' | head
alloy.service
cron.service
loki.service
ssh.service
systemd-journald.service
...
Y recupera las últimas líneas del servicio SSH con una consulta LogQL:
curl -sG http://127.0.0.1:3100/loki/api/v1/query_range \
--data-urlencode 'query={job="systemd-journal", unit="ssh.service"}' \
--data-urlencode 'limit=5' | jq -r '.data.result[].values[][1]'
Paso 6: Consultar los logs con LogQL
LogQL combina un selector de streams entre llaves, que usa las etiquetas, con filtros y parsers que trabajan sobre el contenido. Estos son los patrones más útiles.
Filtrar por texto. Intentos de login SSH fallidos:
{job="systemd-journal", unit="ssh.service"} |= "Failed password"
Filtrar con una expresión regular y excluir líneas:
{job="systemd-journal", level=~"err|crit"} != "CRON"
Extraer campos al consultar. El parser pattern divide las líneas de acceso de Nginx en campos sin necesidad de etiquetarlos al ingerir, y después puedes filtrar por ellos:
{job="nginx"} | pattern `<ip> - - <_> "<method> <uri> <_>" <status> <size> <_> "<agent>" <_>` | status >= 500
Generar métricas a partir de logs. Número de líneas por unidad de systemd en ventanas de 5 minutos:
sum by (unit) (count_over_time({job="systemd-journal"}[5m]))
Tasa de respuestas de Nginx por código de estado:
sum by (status) (rate({job="nginx"} | pattern `<_> - - <_> "<_> <_> <_>" <status> <_>` [5m]))
Las consultas con |= son mucho más rápidas que las de |~, y cuanto más concreto sea el selector de etiquetas, menos datos tiene que leer Loki.
Paso 7: Visualizar los logs en Grafana
Instala Grafana:
sudo apt install -y grafana
Registra Loki como fuente de datos mediante un fichero de aprovisionamiento, así la configuración queda en disco y no depende de clics en la interfaz:
sudo nano /etc/grafana/provisioning/datasources/loki.yaml
apiVersion: 1
datasources:
- name: Loki
type: loki
access: proxy
url: http://127.0.0.1:3100
isDefault: true
Arranca Grafana y comprueba su estado:
sudo systemctl enable --now grafana-server
curl -s http://127.0.0.1:3000/api/health | jq -r .database
ok
Permite el acceso al puerto 3000 solo desde tu IP, sustituyendo your_admin_ip:
sudo ufw allow from your_admin_ip to any port 3000 proto tcp
Abre http://your_server_ip:3000, entra con admin / admin y define una contraseña nueva cuando Grafana te la pida. En Explore, con la fuente Loki seleccionada, cambia el editor a Code y ejecuta cualquiera de las consultas del paso 6. Las consultas de métricas se muestran como gráfica y las de logs como lista de líneas. Desde Explore puedes añadir cualquier consulta a un dashboard con Add to dashboard.
Paso 8: Migrar una configuración de Promtail a Alloy
Si ya tienes servidores con Promtail, Alloy puede traducir su configuración automáticamente. En el servidor con Promtail, instala Alloy desde el mismo repositorio (paso 1 y paso 3) y ejecuta:
sudo alloy convert --source-format=promtail --output=/etc/alloy/config.alloy /etc/promtail/config.yml
Cambia /etc/promtail/config.yml por la ruta real de tu fichero de Promtail. Revisa el resultado, ya que algunas opciones poco comunes pueden requerir ajustes manuales. Después detén Promtail y arranca Alloy:
sudo systemctl disable --now promtail
sudo systemctl restart alloy
Alloy guarda sus propias posiciones de lectura, así que puede reenviar líneas que Promtail ya había enviado. Para evitarlo, añade tail_from_end = true al componente loki.source.file que genera la conversión.
Solución de problemas
Loki no arranca con mkdir /var/lib/loki/...: permission denied. El directorio de datos no pertenece al usuario loki. Ejecuta sudo chown -R loki:loki /var/lib/loki y reinicia.
Alloy muestra permission denied al abrir ficheros o el journal. El usuario alloy no está en los grupos adm o systemd-journal, o el servicio no se ha reiniciado desde que lo añadiste. Compruébalo con id alloy y reinicia alloy.
Loki rechaza entradas con entry too far behind o greater than max age. Alloy intenta enviar logs más antiguos que el límite de Loki, algo habitual en la primera lectura de ficheros grandes. Son avisos sobre líneas antiguas; los logs nuevos se reciben con normalidad.
Error maximum active stream limit exceeded. Hay demasiadas combinaciones de etiquetas, normalmente por usar como etiqueta un valor con muchos valores posibles. Revisa las reglas de loki.relabel y las etiquetas de path_targets.
Conclusión
Tienes Loki almacenando los logs de tu servidor con 31 días de retención, Alloy recogiendo el journal y los ficheros de Nginx en lugar de Promtail, y Grafana para consultarlos con LogQL. Como siguientes pasos, instala Alloy en el resto de servidores apuntando a este Loki a través de la red privada, configura alertas en Grafana sobre consultas de métricas como la tasa de errores 5xx, y mueve el almacenamiento de Loki a un bucket S3 compatible cuando el volumen de logs crezca.
