Every container in your homelab produces stdout and stderr logs by
default. Leave them unconfigured and they pile up in
/var/lib/docker/containers/<id>/<id>-json.log — an unrestricted, never-
rotated file that will quietly fill your disk until services crash or
docker logs stops responding.
I learned this the hard way when a misbehaving Nginx container generated 14 GB of access logs in a weekend. The Proxmox VM ran out of space, PostgreSQL refused to start, and Grafana went blank. That’s when Docker logging stopped being an afterthought and became a core part of the homelab setup.
This guide covers everything you need: log drivers and rotation to contain the noise, log forwarding to Loki via Grafana Alloy for centralized viewing, and practical configurations you can copy into your Compose files today.
Docker Log Drivers — How Logs Flow
Docker has a pluggable logging architecture. Every container writes
stdout/stderr to a log driver, which handles delivery and storage.
The default driver is json-file — it writes each log line as a JSON
object in a single file on the host.
Check your current default driver:
|
|
Key log drivers for the homelab:
| Driver | Storage | Use Case |
|---|---|---|
json-file |
Host filesystem | Default; needs rotation config |
local |
Host filesystem | Leaner than json-file, no JSON wrapping |
journald |
systemd journal | Integrates with host logging |
syslog |
Remote syslog server | Forward to rsyslog or syslog-ng |
fluentd |
Fluentd daemon | Forward to log aggregators |
gelf |
Graylog/GELF endpoint | Graylog ecosystem |
loki |
Grafana Loki | Direct push to Loki (plugin) |
none |
Nowhere | Silent containers (dev/test only) |
For a single-node homelab, configure json-file with limits. For
multi-host or observability-focused setups, forward everything to Loki.
Log Rotation — Stop Disk Exhaustion
Without rotation limits, Docker’s json-file driver writes until the disk
is full. Set max-size and max-file to cap log growth.
Configure Globally in daemon.json
|
|
Save to /etc/docker/daemon.json and restart Docker:
|
|
This applies to all new containers. Existing containers keep their old settings until recreated.
Configure Per-Container in Docker Compose
Fine-grained control per service — databases get more file count, chatty apps get tighter caps:
|
|
Use YAML anchors to avoid repeating config:
|
|
Clean Up Existing Bloated Logs
If you already have logs eating disk, find and truncate them:
|
|
⚠️ Never delete the log files directly — Docker holds file handles. Truncate with
truncate -s 0to release disk space without restarting the container.
The Local Driver — A Leaner Default
Docker’s local driver is a drop-in replacement for json-file that
writes raw text instead of JSON, uses ~40% less disk, and still supports
docker logs.
|
|
Trade-off: docker logs works, but parsing tools that expect JSON
log files won’t. Most production log pipelines (Loki, Grafana Alloy)
consume via the Docker API or socket — they don’t read the log files
directly.
For a pure-homelab setup where you’re not feeding logs to an external
parser that needs json-file, local is the better default.
Centralized Logging with Loki and Grafana Alloy
Log rotation stops disk problems, but you lose visibility. When a container crashes overnight, its rotated logs are gone by morning. Centralized logging captures everything in a searchable, persistent store.
The stack: Grafana Alloy collects logs from the Docker socket and forwards them to Loki. Grafana queries Loki for visualization and alerting.
Deploy Loki
|
|
Minimal Loki config:
|
|
Collect Logs with Grafana Alloy
Grafana Alloy watches the Docker socket for running containers, discovers their log streams, and forwards them to Loki.
|
|
Alloy config for Docker log collection:
|
|
Query Logs in Grafana
Add Loki as a data source in Grafana (http://loki:3100). Then query
logs with LogQL:
# All logs from the Nginx container in the last hour
{container_name="nginx"} |= ``
# Error logs from Postgres in the last 24 hours
{container_name="postgres"} |= "ERROR"
# Logs with a specific image, filter to warnings and above
{image="prom/prometheus:latest"} |~ "(warn|error|fatal|panic)"
# Rate of errors per container in the last 15 minutes
sum by(container_name) (rate({job="docker"} |= "error"[5m]))
Create alert rules in Grafana for common log patterns:
|
|
Docker Log Tags — Add Context to Every Line
The tag log option appends metadata that Loki and other collectors
can use for filtering:
|
|
Built-in template variables:
| Variable | Expands To |
|---|---|
{{.Name}} |
Container name |
{{.ID}} |
Full container ID |
{{.ImageName}} |
Image name with tag |
{{.ShortID}} |
First 12 chars of container ID |
{{.FullID}} |
Full 64-char container ID |
Practical tag format for homelab:
|
|
This lets you filter by compose project and service name in Loki:
{container_name="grafana", compose_project="monitoring"}
Logging with the Docker Socket Proxy
If you follow Docker socket security best practices (covered in the Docker Socket Proxy post), Grafana Alloy can talk to the socket proxy instead of the raw socket:
|
|
This keeps the security benefits of socket proxying while still enabling
log discovery. Make sure your socket proxy allows the /containers
endpoint for log scraping.
Log Size Benchmarks — Real Numbers
Tested on a homelab running 12 containers for one week:
| Driver | Config | Disk Used | docker logs Works |
|---|---|---|---|
json-file |
default (no limits) | 3.2 GB | ✅ |
json-file |
max-size=10m, max-file=3 | 180 MB | ✅ (last 3 files) |
local |
max-size=10m, max-file=3 | 108 MB | ✅ |
journald |
default | 420 MB (shared journal) | ✅ (via journalctl) |
json-file + Loki |
max-size=10m, max-file=3 | 180 MB local + ~90 MB Loki | ✅ |
The difference between “no limits” and “configured” is a 17x reduction in disk usage for this workload. On a Raspberry Pi or a small Proxmox VM with limited storage, that’s the difference between a stable system and weekly alerts.
Summary: A Logging Strategy for Your Homelab
Start here — a single Compose overlay that sets sane defaults:
|
|
Then, over a weekend:
- Add this anchor to every Compose file
- Deploy Loki + Grafana Alloy to centralize logs
- Create a Loki data source in Grafana
- Set up a couple of LogQL alert rules for errors and crash loops
The three pillars of Docker log management:
- Rotate — Always set
max-sizeandmax-file. Disk space is a finite resource in the homelab. Default logs will consume it all. - Centralize — Even a single-node Loki + Alloy stack turns scattered container logs into a searchable, alertable system.
- Tag — Add container names, project labels, and image info to log entries. Makes Loki queries 10x more useful.
Once this is in place, you’ll never wonder what happened overnight again. The logs are there, searchable, and alerting you before disk fills up.