The most frustrating experience in systems administration is knowing an anomaly occurred—a switch port flapped, a rogue IoT device attempted an unauthorized DHCP request, or an edge firewall dropped a burst of malformed packets—and having to SSH into five different physical appliances to grep through disjointed text logs. When you operate 10 separate VLANs and dozens of microservices, decentralized logging is an operational liability. I solved this by deploying a centralized, low-overhead syslog pipeline using Grafana Loki and Promtail.
Traditional log aggregation engines like Elasticsearch (ELK) or Splunk are notorious resource hogs that demand 16GB of RAM just to idle. In contrast, Grafana Loki takes a radically efficient approach: it doesn’t build expensive full-text inverted indexes on every word. Instead, it indexes only metadata labels (like hostname, facility, and severity) and compresses the raw log chunks. Here is how I set up Loki and Promtail on docker02, routed our UniFi hardware logs, and hooked up single sign-on via Authentik.
1. The Architecture: Promtail as the Universal Collector
Our centralized logging topology consists of three lightweight containerized services running on docker02 (IP 10.1.20.19):
- Promtail (Port 514 UDP/TCP): Acts as the network syslog receiver. It listens for incoming RFC-3164 (legacy BSD) and RFC-5424 (modern IETF) syslog packets from our UniFi Dream Machine, 48-port PoE switches, and access points. It extracts structured labels and streams the logs into Loki.
- Loki (Port 3100): The log database engine. It stores raw log streams compressed in filesystem chunks, indexing only the stream labels. Memory usage remains under 350MB even under heavy log bursts.
- Grafana (Port 3000): The dashboard and query engine. It connects to Loki as an authoritative data source and allows rapid LogQL filtering and alerting.
2. Production Docker Compose & SSO Integration
Security is paramount. Rather than relying on local username/password accounts for Grafana, we integrated it directly with our centralized identity provider (Authentik) using OAuth 2.0 with PKCE:
# /opt/stacks/syslog/docker-compose.yml excerpt
services:
loki:
image: grafana/loki:latest
ports:
- "3100:3100"
command: -config.file=/etc/loki/local-config.yaml
volumes:
- loki-data:/loki
restart: unless-stopped
promtail:
image: grafana/promtail:latest
volumes:
- ./promtail-config.yaml:/etc/promtail/config.yml
- /var/log:/var/log:ro
command: -config.file=/etc/promtail/config.yml
ports:
- "514:1514/udp"
- "514:1514/tcp"
restart: unless-stopped
grafana:
image: grafana/grafana:latest
environment:
- GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_ADMIN_PASSWORD}
- GF_SERVER_ROOT_URL=http://10.1.20.19:3000
- GF_AUTH_GENERIC_OAUTH_ENABLED=true
- GF_AUTH_GENERIC_OAUTH_NAME=IT Tech STL Sign-In
- GF_AUTH_GENERIC_OAUTH_CLIENT_ID=${GRAFANA_OAUTH_ID}
- GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET=${GRAFANA_OAUTH_SECRET}
- GF_AUTH_GENERIC_OAUTH_SCOPES=openid email profile
- GF_AUTH_GENERIC_OAUTH_AUTH_URL=https://auth.ittechstl.com/application/o/authorize/
- GF_AUTH_GENERIC_OAUTH_TOKEN_URL=https://auth.ittechstl.com/application/o/token/
- GF_AUTH_GENERIC_OAUTH_API_URL=https://auth.ittechstl.com/application/o/userinfo/
- GF_AUTH_GENERIC_OAUTH_ROLE_ATTRIBUTE_PATH=contains(groups[*], 'authentik Admins') && 'Admin' || 'Editor'
ports:
- "3000:3000"
volumes:
- grafana-data:/var/lib/grafana
restart: unless-stopped
3. Configuring Promtail for Network Syslog Ingest
To accept syslog traffic from our UniFi switches and gateways, Promtail must be configured with a syslog scrape job. The configuration file below listens on internal port 1514, strips timestamps, and dynamically assigns the origin IP as the hostname label:
# promtail-config.yaml
server:
http_listen_port: 9080
grpc_listen_port: 0
positions:
filename: /tmp/positions.yaml
clients:
- url: http://loki:3100/loki/api/v1/push
scrape_jobs:
# Ingest local Linux system logs
- job_name: system
static_configs:
- targets:
- localhost
labels:
job: varlog
host: docker02
__path__: /var/log/*.log
# Ingest remote syslog from UniFi hardware
- job_name: unifi_syslog
syslog:
listen_address: 0.0.0.0:1514
idle_timeout: 60s
label_structured_data: yes
labels:
job: syslog
relabel_configs:
- source_labels: ['__syslog_message_hostname']
target_label: 'host'
- source_labels: ['__syslog_message_severity']
target_label: 'severity'
4. Directing UniFi to the Central Log Target
Routing telemetry from Ubiquiti hardware into our Docker container requires just two clicks in the UniFi Network Controller:
- Navigate to Settings > System > Advanced > Remote Logging.
- Enable Syslog, enter the IP address of
docker02(10.1.20.19), set the port to514, and check Device Activities and Firewall Rule Triggers.
Within seconds, Promtail begins ingesting thousands of event streams—spanning everything from 802.1X Wi-Fi client handoffs to inter-VLAN firewall rule drops.
5. Real-World LogQL Queries That Save Hours
The Power of LogQL: Instead of opening terminal sessions and scrolling through hundreds of thousands of lines, you write stream queries directly in Grafana’s Explore tab.
Here are three LogQL queries I use weekly to diagnose network anomalies:
1. Isolating Dropped Firewall Traffic:
{job="syslog"} |= "UDM-Pro" |= "DROP" | pattern "<_> kernel: DROP: IN= OUT= MAC= SRC= DST= PROTO="
2. Detecting Flapping Ethernet Switch Ports:
{job="syslog"} |= "switch" |~ "(link down|link up|STP state changed)"
3. Tracking DHCP Exhaustion or Clashes:
{job="syslog"} |~ "dnsmasq-dhcp\\[.*\\]: (DHCPNAK|DHCPDECLINE)"
6. Retention & Compaction Strategy
To ensure logs don’t silently fill the system drive, we configured Loki’s compaction engine with a strict 30-day retention window. Older chunks are automatically pruned every 24 hours. Because Loki compresses log text using Snappy compression, 30 days of continuous network logs across 40+ endpoints occupies less than 8GB of disk space.
No more guessing why a Wi-Fi client dropped or whether an IoT sensor attempted to telephone abroad. Everything is indexed, searchable, and visualized in a single unified dashboard.