Implementing Loki across 20+ bare-metal or VM servers running Docker Compose requires an architectural choice. You have two main approaches to collect logs and forward them to a central Loki instance:
- The Modern Agent Approach (Grafana Alloy via Docker Bind-Mounts): Run Grafana Alloy as a standalone container on each of your 20 servers. It automatically sniffs out the log files from
/var/lib/docker/containers/and ships them to Loki. (Recommended for stability and filtering power). - The Plugin Approach (Loki Docker Driver): Install a Docker plugin on all 20 hosts to direct the Docker engine itself to stream logs directly to Loki over HTTP.
Here is the step-by-step blueprint for Approach 1 (Grafana Alloy), which is the most reliable production setup because it won’t crash or lock up your Docker daemon if your central Loki server goes down temporarily.
Step 1: Set Up the Central Loki + Grafana Server
Pick one of your 20 servers (or a dedicated monitoring server) to act as your Central Observability Hub.
Create a docker-compose.yml file on that central machine:
YAML
version: "3.8"services: loki: image: grafana/loki:3.0.0 ports: - "3100:3100" volumes: - loki-data:/loki command: -config.file=/etc/loki/local-config.yaml restart: unless-stopped grafana: image: grafana/grafana:latest ports: - "3000:3000" volumes: - grafana-data:/var/lib/grafana environment: - GF_SECURITY_ADMIN_PASSWORD=your_secure_password restart: unless-stoppedvolumes: loki-data: grafana-data:
Run docker compose up -d on this central server. Ensure port 3100 is reachable over your internal network from the other 19 servers.
Step 2: Deploy Grafana Alloy on the 19 Remote Servers
On each of your other servers, you will deploy a tiny Grafana Alloy container. Its job is to read the standard JSON logs Docker naturally writes to the host disk.
1. Create the Alloy Config File
Create a directory /etc/alloy/ on the host, and save the following as config.alloy. This configuration file auto-discovers all running Docker containers and forwards their streams to your central Loki instance.
Code snippet
// Discover all local running Docker containersdiscovery.docker "local_containers" { host = "unix:///var/run/docker.sock"}// Read the log files of those discovered containersloki.source.docker "docker_logs" { targets = discovery.docker.local_containers.targets forward_to = [loki.write.central_loki.receiver]}// Send the logs to your central Loki instanceloki.write "central_loki" { endpoint { url = "http://<CENTRAL_LOKI_IP>:3100/loki/api/v1/push" }}
(Replace <CENTRAL_LOKI_IP> with the actual internal network IP of your central monitoring server).
2. Launch Alloy via Docker Compose
On each server, deploy Alloy using this lightweight compose file. It maps the host’s Docker socket and log paths into the Alloy container:
YAML
version: "3.8"services: alloy: image: grafana/alloy:latest container_name: alloy-logger volumes: - /etc/alloy/config.alloy:/etc/alloy/config.alloy:ro - /var/run/docker.sock:/var/run/docker.sock:ro - /var/lib/docker/containers:/var/lib/docker/containers:ro command: run --storage.path=/var/lib/alloy/data /etc/alloy/config.alloy network_mode: host restart: unless-stopped
Run docker compose up -d. Alloy will immediately start streaming logs to your central hub.
Step 3: View the Logs in Grafana
- Open your browser and head to
http://<CENTRAL_SERVER_IP>:3000(Log in using the admin password you set in Step 1). - Navigate to Connections -> Data Sources and click Add data source.
- Select Loki.
- In the URL field, type
http://loki:3100(orhttp://localhost:3100) and click Save & test. - Go to the Explore tab on the left sidebar, select your Loki data source, and use the label browser to query your logs:
Code snippet
# Find logs from a specific container name across your fleet{container_name="authentik-worker"}
Pro-Tips for Managing 20+ Servers
- Automate with Ansible: Don’t configure 20 servers manually! Write a quick Ansible playbook to drop the
config.alloyanddocker-compose.ymlonto all 19 target machines and rundocker compose up -d. - Add a
hostLabel: If you want to differentiate which logs came from which physical server, add an environment variable to Alloy or adjust theconfig.alloyfile to inject a statichost="server-01"metric label. - Keep
json-fileRotation Enabled: Because Alloy reads the raw logs from/var/lib/docker/containers, make sure your existing application compose files don’t have log rotation disabled. Set a global limit in/etc/docker/daemon.jsonon your hosts to prevent logs from filling up local disks:JSON{ "log-driver": "json-file", "log-opts": { "max-size": "50m", "max-file": "3" } }