Alloy
Alloy is an open-source OpenTelemetry collector by Grafana Labs for collecting, processing, and exporting telemetry data such as logs, metrics, and traces.
This compose file deploys Alloy as a log collector that ships Docker container logs and Linux journal logs to a Loki instance. It mounts the Docker socket for docker logs and journal directory for journal logs, with its configuration stored on NFS. The Alloy dashboard is proxied through a Traefik reverse proxy with Cloudflare TLS certificates. An example Alloy configuration file is included that sends the collected logs to a Loki instance.
For more information, check out the official documentation.
Docker Compose
# compose.yaml
services:
alloy:
image: grafana/alloy:latest
container_name: alloy
restart: unless-stopped
# ports:
# - 12345:12345
networks:
- alloy_proxy
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /var/log/journal:/var/log/journal:ro
- type: volume
source: docker-nfs
target: /etc/alloy
volume:
subpath: alloy/config
command:
- run
- /etc/alloy/config.alloy
- --storage.path=/var/lib/alloy/data
- --server.http.listen-addr=0.0.0.0:12345
- --stability.level=generally-available
environment:
LOKI_HOST: ${LOKI_HOST}
DOCKER_HOST: ${DOCKER_HOST}
TZ: Europe/London
labels:
- "traefik.enable=true"
- "traefik.docker.network=alloy_proxy"
- "traefik.http.services.alloy.loadbalancer.server.port=12345"
- "traefik.http.routers.alloy.rule=Host(`alloy.${TRAEFIK_BASE_URL}`)"
- "traefik.http.routers.alloy.entrypoints=websecure"
- "traefik.http.routers.alloy.tls.certresolver=cloudflare"
volumes:
docker-nfs:
driver: local
driver_opts:
type: nfs
o: addr=xxx.xxx.xxx.xxx,nolock,soft,rw,nfsvers=4.2
device: :/mnt/nfs-volume
networks:
alloy_proxy:
name: alloy_proxy
Environment Variables
# .env
TRAEFIK_BASE_URL=example.com
DOCKER_HOST=VM_NAME
LOKI_HOST=loki.example.com
Configuration
The Alloy configuration file is found in the mounted /etc/alloy directory.
// config.alloy
logging {
level = "info"
format = "logfmt"
write_to = [loki.relabel.alloy_logs.receiver]
}
discovery.docker "local_docker" {
host = "unix:///var/run/docker.sock"
}
loki.source.journal "read" {
forward_to = [ loki.relabel.journal_labels.receiver ]
labels = {
application = "journald",
host = sys.env("DOCKER_HOST"),
}
path = "/var/log/journal"
}
loki.relabel "journal_labels" {
forward_to = [loki.write.lokiout.receiver]
rule {
source_labels = ["__journal__systemd_unit"]
target_label = "unit"
}
rule {
source_labels = ["__journal__transport"]
target_label = "transport"
}
rule {
source_labels = ["__journal_priority_keyword"]
target_label = "priority"
}
}
discovery.relabel "docker_labels" {
targets = []
rule {
source_labels = ["__meta_docker_container_name"]
target_label = "container"
regex = "/(.*)"
replacement = "$1"
}
rule {
source_labels = ["__meta_docker_container_id"]
target_label = "container_id"
}
}
loki.source.docker "docker_engine" {
host = "unix:///var/run/docker.sock"
targets = discovery.docker.local_docker.targets
labels = {
application = "docker",
host = sys.env("DOCKER_HOST"),
}
relabel_rules = discovery.relabel.docker_labels.rules
forward_to = [loki.write.lokiout.receiver]
}
loki.write "lokiout" {
endpoint {
url = "https://loki.example.com/loki/api/v1/push"
}
}
loki.relabel "alloy_logs" {
forward_to = [loki.write.lokiout.receiver]
rule {
target_label = "service"
replacement = "alloy"
}
rule {
target_label = "host"
replacement = sys.env("DOCKER_HOST")
}
rule {
target_label = "application"
replacement = "alloy"
}
}
Traefik Configuration
# compose.yaml (excerpt)
services:
traefik:
image: traefik:latest
container_name: traefik
...
networks:
- traefik
# here
- alloy_proxy
...
networks:
# here
alloy_proxy:
name: alloy_proxy