Add logging stack: Loki + Alloy + Grafana for router/switch/phone syslog

Alloy receives syslog on 514/udp and 1514/tcp from network devices,
forwards to a local Loki instance; Grafana provisioned with Loki as
default datasource. Compose bind-mounts config from
/mnt/containers/logging/... on the host (staged separately) since
Portainer's GitOps pull only fetches the compose file, not the repo's
other files.
This commit is contained in:
2026-07-15 11:41:00 +03:00
parent c3589732f2
commit 22dfb732d3
4 changed files with 176 additions and 0 deletions
+64
View File
@@ -0,0 +1,64 @@
// Receives syslog from routers, switches, and Yealink phones on this host's
// LAN interface, tags each message with useful labels, and ships it to the
// local Loki instance.
//
// Point network devices at this host's IP, port 514/udp (or 1514/tcp for
// devices that only speak TCP framing).
loki.relabel "syslog" {
forward_to = []
rule {
source_labels = ["__syslog_message_hostname"]
target_label = "hostname"
}
rule {
source_labels = ["__syslog_message_severity"]
target_label = "severity"
}
rule {
source_labels = ["__syslog_message_facility"]
target_label = "facility"
}
rule {
source_labels = ["__syslog_connection_ip_address"]
target_label = "source_ip"
}
}
loki.source.syslog "network_devices_udp" {
listener {
address = "0.0.0.0:514"
protocol = "udp"
labels = {
job = "syslog",
transport = "udp",
}
}
relabel_rules = loki.relabel.syslog.rules
forward_to = [loki.write.default.receiver]
}
loki.source.syslog "network_devices_tcp" {
listener {
address = "0.0.0.0:1514"
protocol = "tcp"
labels = {
job = "syslog",
transport = "tcp",
}
}
relabel_rules = loki.relabel.syslog.rules
forward_to = [loki.write.default.receiver]
}
loki.write "default" {
endpoint {
url = "http://loki:3100/loki/api/v1/push"
}
}
+56
View File
@@ -0,0 +1,56 @@
version: '3.8'
services:
loki:
image: grafana/loki:latest
container_name: loki
command: -config.file=/etc/loki/config.yaml
restart: unless-stopped
volumes:
- /mnt/containers/logging/loki-config/config.yaml:/etc/loki/config.yaml:Z
- /mnt/containers/logging/loki-data:/loki:Z
networks:
- logging-nw
alloy:
image: grafana/alloy:latest
container_name: alloy
command:
- run
- --server.http.listen-addr=0.0.0.0:12345
- --storage.path=/var/lib/alloy/data
- /etc/alloy/config.alloy
restart: unless-stopped
depends_on:
- loki
volumes:
- /mnt/containers/logging/alloy-config/config.alloy:/etc/alloy/config.alloy:Z
- /mnt/containers/logging/alloy-data:/var/lib/alloy/data:Z
ports:
# syslog intake for routers/switches/phones (UDP, RFC3164/5424)
- '514:514/udp'
# optional TCP syslog for devices that don't do UDP
- '1514:1514/tcp'
networks:
- logging-nw
grafana:
image: grafana/grafana:latest
container_name: grafana
restart: unless-stopped
depends_on:
- loki
environment:
- GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_ADMIN_PASSWORD}
- GF_USERS_ALLOW_SIGN_UP=false
volumes:
- /mnt/containers/logging/grafana-data:/var/lib/grafana:Z
- /mnt/containers/logging/grafana-config/provisioning/datasources:/etc/grafana/provisioning/datasources:Z
networks:
- logging-nw
- reverseproxy-nw
networks:
logging-nw:
reverseproxy-nw:
external: true
@@ -0,0 +1,9 @@
apiVersion: 1
datasources:
- name: Loki
type: loki
access: proxy
url: http://loki:3100
isDefault: true
editable: false
+47
View File
@@ -0,0 +1,47 @@
auth_enabled: false
server:
http_listen_port: 3100
grpc_listen_port: 9096
log_level: info
common:
path_prefix: /loki
storage:
filesystem:
chunks_directory: /loki/chunks
rules_directory: /loki/rules
replication_factor: 1
ring:
instance_addr: 127.0.0.1
kvstore:
store: inmemory
schema_config:
configs:
- from: 2024-01-01
store: tsdb
object_store: filesystem
schema: v13
index:
prefix: index_
period: 24h
limits_config:
reject_old_samples: true
reject_old_samples_max_age: 168h
retention_period: 2160h # 90 days — adjust to taste, disk on this host is ~42GB free
max_query_series: 5000
compactor:
working_directory: /loki/compactor
compaction_interval: 10m
retention_enabled: true
retention_delete_delay: 2h
delete_request_store: filesystem
ruler:
storage:
type: local
local:
directory: /loki/rules