Optimizing Docker Logs with Loki and Grafana

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:

  1. 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).
  2. 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-stopped
volumes:
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 containers
discovery.docker "local_containers" {
host = "unix:///var/run/docker.sock"
}
// Read the log files of those discovered containers
loki.source.docker "docker_logs" {
targets = discovery.docker.local_containers.targets
forward_to = [loki.write.central_loki.receiver]
}
// Send the logs to your central Loki instance
loki.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

  1. Open your browser and head to http://<CENTRAL_SERVER_IP>:3000 (Log in using the admin password you set in Step 1).
  2. Navigate to Connections -> Data Sources and click Add data source.
  3. Select Loki.
  4. In the URL field, type http://loki:3100 (or http://localhost:3100) and click Save & test.
  5. 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.alloy and docker-compose.yml onto all 19 target machines and run docker compose up -d.
  • Add a host Label: If you want to differentiate which logs came from which physical server, add an environment variable to Alloy or adjust the config.alloy file to inject a static host="server-01" metric label.
  • Keep json-file Rotation 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.json on your hosts to prevent logs from filling up local disks:JSON{ "log-driver": "json-file", "log-opts": { "max-size": "50m", "max-file": "3" } }

Understanding Vector for OpenShift Logging

Vector Explained

Vector is a high-performance observability data pipeline developed by Datadog Vector.

Think of it as:

A modern replacement for Fluentd, Fluent Bit, Logstash, and sometimes Filebeat.

Vector can collect, transform, enrich, filter, and route:

  • Logs
  • Metrics
  • Events
  • Traces

to multiple destinations.


Why OpenShift Uses Vector

Historically:

Pods
|
Fluentd
|
Elasticsearch

Problems:

  • High memory usage
  • Ruby-based
  • Difficult to scale
  • CPU intensive

Modern OpenShift Logging:

Pods
|
Vector
|
Loki

Benefits:

  • Rust-based
  • Extremely fast
  • Lower memory consumption
  • Better Kubernetes integration
  • Easier operations

How Vector Works

Vector consists of three parts:

Sources

Collect data from somewhere.

Examples:

sources:
kubernetes_logs:
type: kubernetes_logs

Sources can read:

  • Kubernetes logs
  • Journald
  • Syslog
  • Files
  • Kafka
  • OpenTelemetry

Transforms

Process data.

Examples:

  • Filter
  • Parse JSON
  • Remove secrets
  • Add labels
  • Enrich metadata

Example:

transforms:
filter_errors:
type: filter

Sinks

Send data somewhere.

Examples:

  • Loki
  • Elasticsearch
  • Splunk
  • Kafka
  • Azure Event Hub
  • S3

Example:

sinks:
loki:
type: loki

OpenShift Logging Architecture

Application Pods
|
|
Vector
|
|
LokiStack
|
|
Grafana

or

Application Pods
|
|
Vector
|
|
Elasticsearch
|
|
Kibana

Vector as a DaemonSet

In Kubernetes/OpenShift:

Node 1
└─ Vector Pod
Node 2
└─ Vector Pod
Node 3
└─ Vector Pod

One Vector instance per node.

This is very similar to:

  • Filebeat
  • Fluent Bit
  • Node Exporter

What Logs Does Vector Collect?

Container logs:

/var/log/containers

Pod logs:

/var/log/pods

Node logs:

journald

Audit logs:

/var/log/audit

Infrastructure logs:

kubelet
crio
ovn

Kubernetes Metadata Enrichment

One of Vector’s biggest advantages.

A log:

{
"message": "Database timeout"
}

becomes:

{
"message":"Database timeout",
"namespace":"banking",
"pod":"payment-api",
"container":"api"
}

This makes searching much easier in Loki or ELK.


Vector Remap Language (VRL)

Vector has its own transformation language.

Example:

Remove sensitive information:

del(.password)
del(.creditcard)

Add hostname:

.hostname = get_hostname!()

Normalize logs:

.level = upcase(.level)

Very useful in enterprise environments.


Real OpenShift Example

Suppose your application generates:

{
"level":"error",
"message":"DB connection failed"
}

Vector:

  1. Reads log
  2. Adds Kubernetes metadata
  3. Parses JSON
  4. Filters sensitive data
  5. Sends to Loki

Final log:

{
"namespace":"banking",
"pod":"payment-api",
"level":"ERROR",
"message":"DB connection failed"
}

Vector vs Fluentd

FeatureVectorFluentd
LanguageRustRuby
CPU UsageLowHigh
Memory UsageLowHigh
SpeedExcellentModerate
Kubernetes SupportExcellentGood
OpenShift FuturePreferredLegacy

Vector vs Filebeat

Since you’ve worked extensively with Filebeat and ELK:

FeatureVectorFilebeat
Kubernetes NativeExcellentGood
ParsingExcellentGood
RoutingExcellentLimited
Multi-DestinationExcellentModerate
Resource UsageLowerModerate
OpenShift IntegrationNativeExternal

Example: Send Logs to Loki

sources:
kubernetes_logs:
type: kubernetes_logs
sinks:
loki:
type: loki
endpoint: http://loki:3100

Flow:

Pod Logs
|
Vector
|
Loki
|
Grafana

Example: Send Logs to Elasticsearch

sources:
kubernetes_logs:
type: kubernetes_logs
sinks:
elasticsearch:
type: elasticsearch
endpoints:
- http://elasticsearch:9200

Flow:

Pod Logs
|
Vector
|
Elasticsearch
|
Kibana

Enterprise OpenShift Deployment

For a banking or government OpenShift platform:

Applications
|
|
Vector
|
+----------------+
| |
v v
Loki SIEM
| |
Grafana Sentinel/Splunk

This provides:

  • Operational logs in Loki
  • Security logs in SIEM
  • Compliance retention
  • Centralized visibility

Interview Question

“What is Vector and why is it used in OpenShift?”

A strong answer:

Vector is a high-performance observability data pipeline written in Rust. It collects logs, metrics, and events from Kubernetes and OpenShift workloads, enriches them with metadata, transforms them using VRL, and routes them to destinations such as Loki, Elasticsearch, Splunk, Kafka, or cloud storage.

Red Hat adopted Vector as the primary log collector in OpenShift Logging because it is significantly more efficient than Fluentd, consumes fewer resources, scales better, and integrates naturally with Kubernetes metadata. In modern OpenShift deployments, Vector typically runs as a DaemonSet on every node, collecting container, infrastructure, and audit logs before forwarding them to LokiStack or an external logging platform.

Architect-Level Follow-Up

If an interviewer asks:

“Why would you choose Vector + Loki instead of Filebeat + ELK?”

Answer:

For OpenShift-centric environments, Vector + Loki provides lower infrastructure cost, lower operational complexity, and better Kubernetes-native metadata handling. For organizations already heavily invested in ELK, advanced full-text searching, SIEM workflows, or existing Elasticsearch expertise, Vector can still be used as the collector while forwarding logs to Elasticsearch. This gives the benefits of Vector’s efficient collection and transformation layer while preserving the existing ELK ecosystem.

If an interviewer asks:

“Would you use Vector or Node Exporter?”

A strong answer is:

They serve different purposes. Node Exporter collects host-level metrics such as CPU, memory, disk, and network usage for Prometheus. Vector collects and forwards logs from applications, containers, and operating systems to platforms like Loki, Elasticsearch, or Splunk. In a production OpenShift or Linux environment, I would deploy both. Node Exporter provides infrastructure observability, while Vector provides log observability. Together they form a complete monitoring and troubleshooting solution.