01Why Centralized Logging Matters
02How the Loki Stack Works
Grafana Alloy (formerly Grafana Agent) is replacing Promtail as the recommended collector. It supports logs, metrics, and traces in a single agent. For new setups, consider using Alloy instead of Promtail. For simplicity, this guide uses Promtail since it's still widely documented and works perfectly for log-only collection.
03Docker Compose Configuration
1services: 2 loki: 3 image: grafana/loki:3.44 command: -config.file=/etc/loki/local-config.yaml5 volumes: 6 - loki-data:/loki7 ports: 8 - "3100:3100"9 restart: unless-stopped10 healthcheck: 11 test: ["CMD", "wget", "--quiet", "--tries=1", "--output-document=-", "http://localhost:3100/ready"]12 interval: 15s13 timeout: 5s14 retries: 51516 promtail: 17 image: grafana/promtail:3.418 volumes: 19 - ./promtail-config.yml:/etc/promtail/config.yml:ro20 - /var/lib/docker/containers:/var/lib/docker/containers:ro21 - /var/run/docker.sock:/var/run/docker.sock:ro22 command: -config.file=/etc/promtail/config.yml23 depends_on: 24 loki: 25 condition: service_healthy26 restart: unless-stopped2728 grafana: 29 image: grafana/grafana:11.5.230 environment: 31 - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD:-admin}32 - GF_AUTH_ANONYMOUS_ENABLED=false33 volumes: 34 - grafana-data:/var/lib/grafana35 ports: 36 - "3000:3000"37 depends_on: 38 loki: 39 condition: service_healthy40 restart: unless-stopped4142volumes: 43 loki-data: 44 grafana-data: 04Configuring Promtail for Docker
1server: 2 http_listen_port: 90803 grpc_listen_port: 045positions: 6 filename: /tmp/positions.yaml78clients: 9 - url: http://loki:3100/loki/api/v1/push1011scrape_configs: 12 - job_name: docker13 docker_sd_configs: 14 - host: unix:///var/run/docker.sock15 refresh_interval: 5s16 relabel_configs: 17 # Extract container name18 - source_labels: ['__meta_docker_container_name']19 regex: '/(.*)'20 target_label: 'container'21 # Extract compose service name22 - source_labels: ['__meta_docker_container_label_com_docker_compose_service']23 target_label: 'service'24 # Extract compose project name25 - source_labels: ['__meta_docker_container_label_com_docker_compose_project']26 target_label: 'project'Watch your log volume. A single verbose service can generate gigabytes of logs per day. Set Docker's log driver options to limit log file sizes: add --log-opt max-size=50m --log-opt max-file=3 to your Docker daemon configuration. In Loki, configure retention to auto-delete old logs — 7-14 days is usually sufficient for troubleshooting.
05Querying Logs with LogQL
1# Basic: show all logs from a specific service2{service="nginx"}34# Filter by keyword5{service="app"} |= "error"67# Exclude noisy lines8{service="app"} != "healthcheck"910# Regex matching11{service="app"} |~ "status=(4|5)\d{2}"1213# Parse JSON logs and filter by field14{service="api"} | json | level="error"1516# Count errors per service over time (for dashboards)17sum(rate({service=~".+"} |= "error" [5m])) by (service)1819# Top 10 noisiest services20topk(10, sum(rate({service=~".+"} [1h])) by (service))