A guide to collect Docker and host logs using Grafana Alloy + Loki, and why Alloy is replacing Promtail. Includes diagrams, permissions, docker-compose, and config files.
Containers made deployments fast, portable, and efficient.
But collecting logs from hosts + containers + services quickly becomes chaotic.
That’s why the modern stack uses Grafana Alloy + Loki instead of the older Promtail-based approach.
🧠 Why Grafana Loki Uses Alloy Instead of Promtail
Promtail served its purpose well — but infrastructure evolved.
Grafana introduced Alloy as its unified, modern, extensible telemetry collector.
🔥 Promtail vs Alloy (Honest Breakdown)
| Feature | Promtail | Alloy |
|---|---|---|
| Log Collection | ✔️ Yes | ✔️ Yes |
| Metrics Collection | ❌ No | ✔️ Yes |
| Traces Collection | ❌ No | ✔️ Yes |
| Unified pipeline | ❌ | ✔️ Logs + Metrics + Traces |
| Docker auto-discovery | Limited | ✔️ Excellent |
| Processing pipeline | Basic | ✔️ Powerful |
| Extensibility | Low | ✔️ High |
| Future support | 🟥 Legacy | 🟩 Actively developed |
📌 The real reason Alloy replaces Promtail
Promtail = log-only agent
Alloy = one collector for logs, metrics, and traces
Promtail is now maintenance-only.
Alloy is the future.
🧩 Architecture Overview
The logging pipeline includes:
- Alloy → collector
- Loki → log store
- Grafana → visualization
🔥 High-level workflow
Host Logs → Alloy → Loki → Grafana
Docker Logs → Alloy → Loki → Grafana
🧱 Architecture Diagram (Text View)
┌───────────────────────┐
│ Grafana │
│ (Dashboards + Logs) │
└──────────┬────────────┘
│
┌────────┴─────────┐
│ Loki │
│ (Log Store) │
└────────┬─────────┘
│
┌────────────────┴────────────────┐
│ Alloy │
│ (Unified Telemetry Collector) │
└───────────┬───────────┬─────────┘
│ │
Host Logs Docker Logs
(/var/log/*) (via Docker Engine)
(/var/lib/docker/containers/*)
📁 Folder Structure
/opt/alloy/
├── alloy/
│ └── config.alloy
├── grafana-data/
├── loki/
│ └── loki-config.yaml
├── loki-data/
└── docker-compose.yml
🔐 Required Permissions (Critical)
Loki & Grafana will fail without correct folder ownership.
Loki storage (UID/GID 10001)
sudo mkdir -p /opt/alloy/loki-data
sudo chown -R 10001:10001 /opt/alloy/loki-data
sudo mkdir -p /opt/alloy/grafana-data
sudo chown -R 472:472 /opt/alloy/grafana-data
🐳 docker-compose.yaml (Production Ready)
services:
loki:
image: grafana/loki:3.1.0
container_name: loki
command: -config.file=/etc/loki/local-config.yaml
volumes:
- ./loki/loki-config.yaml:/etc/loki/loki-config.yaml:ro
- ./loki-data:/loki
ports:
- "3100:3100"
alloy:
image: grafana/alloy:latest
container_name: alloy
privileged: true
volumes:
- ./alloy/config.alloy:/etc/alloy/config.alloy:ro
- /var/run/docker.sock:/var/run/docker.sock
- /var/log:/var/log:ro
ports:
- "12345:12345"
grafana:
image: grafana/grafana:latest
container_name: grafana
volumes:
- ./grafana-data:/var/lib/grafana
ports:
- "3000:3000"
⚙️ config.alloy
### Send Alloy’s internal logs to Loki
logging {
level = "info"
format = "logfmt"
write_to = [loki.relabel.alloy_logs.receiver]
}
### Auto-discover Docker containers
discovery.docker "local_docker" {
host = "unix:///var/run/docker.sock"
}
### Match host logs (/var/log/*log)
local.file_match "host_logs" {
path_targets = [
{ __path__ = "/var/log/*log" },
]
}
### Tail host logs
loki.source.file "host_tail" {
targets = local.file_match.host_logs.targets
forward_to = [loki.process.host_pipeline.receiver]
}
### Add static labels to host logs
loki.process "host_pipeline" {
forward_to = [loki.write.local.receiver]
stage.static_labels {
values = {
job = "varlogs",
source = "host",
env = "dev",
}
}
}
### Collect logs from Docker containers (stdout/stderr)
loki.source.docker "docker_engine" {
host = "unix:///var/run/docker.sock"
targets = discovery.docker.local_docker.targets
labels = {
job = "docker_logs",
env = "dev",
cluster = "local",
}
forward_to = [loki.write.local.receiver]
}
### Loki ingest endpoint
loki.write "local" {
endpoint {
url = "http://loki:3100/loki/api/v1/push"
}
}
### Add label to internal Alloy logs
loki.relabel "alloy_logs" {
forward_to = [loki.write.local.receiver]
rule {
target_label = "service"
replacement = "alloy"
}
}
loki-config.yaml
auth_enabled: false
server:
http_listen_port: 3100
grpc_listen_port: 9096
common:
instance_addr: 127.0.0.1
path_prefix: /loki
storage:
filesystem:
chunks_directory: /loki/chunks
rules_directory: /loki/rules
replication_factor: 1
ring:
kvstore:
store: inmemory
query_range:
results_cache:
cache:
embedded_cache:
enabled: true
max_size_mb: 100
limits_config:
ingestion_rate_mb: 10
ingestion_burst_size_mb: 20
max_concurrent_tail_requests: 20
max_cache_freshness_per_query: 10m
max_streams_per_user: 50
allow_structured_metadata: false
schema_config:
configs:
- from: 2024-01-01
store: tsdb
object_store: filesystem
schema: v13
index:
prefix: index_
period: 24h
🚀 Restart and Run
docker compose up -d
- ✔ All Docker logs collected
- ✔ All host logs collected
- ✔ Alloy logs included with label
service=alloy - ✔ Grafana fully functional
- ✔ No permission errors
- ✔ Production-grade observability pipeline
Alloy replaces Promtail and simplifies the entire telemetry ecosystem — logs, metrics, traces — in one place.

Top comments (0)