DockerSelf-Hosting

Docker Logs: A Complete Beginner's Guide to Container Logging in Your Homelab

Learn how to view, manage, and centralize Docker container logs. Covers docker logs command, log rotation, disk space fixes, and Promtail + Loki + Grafana setup for homelabs.

AU

Author

David Okonkwo

Disclosure: This article may contain affiliate links. If you purchase through these links, we may earn a commission at no additional cost to you. We only recommend products we have personally tested or thoroughly researched.

Key Takeaways

  • The docker logs command is your first tool for debugging any container - learn the flags that matter (-f, --tail, --since, -t).
  • Docker's default logging driver stores logs without rotation - configure max-size and max-file before logs fill your disk.
  • You can set log rotation globally in /etc/docker/daemon.json or per-container in Docker Compose files.
  • When your homelab grows past 10-15 containers, centralized logging with Promtail, Loki, and Grafana becomes essential.
  • The local logging driver is more efficient than json-file for most homelab setups.

If you've ever SSH'd into your homelab server, noticed your disk was mysteriously full, and traced it back to a container that quietly wrote 50GB of logs - you're not alone. Docker logging is one of those topics that nobody thinks about until something goes wrong. And when it goes wrong at 2 AM, you want to know exactly how to fix it.

This guide walks you through everything you need to know about Docker logs - from reading your first log entry to setting up centralized logging across your entire homelab. We'll go step by step, and I'll explain why each thing matters before we do it.

What Are Docker Container Logs (and Why Should You Care)?

When a Docker container runs, it writes output to two streams: stdout (standard output) and stderr (standard error). Docker captures both of these and stores them as logs. Think of it like a notepad that records everything a program prints to the screen - except Docker keeps the notepad even after the program exits.

Why this matters for your homelab:

  • Troubleshooting: When a container stops working, logs tell you why.
  • Monitoring: Regular log checks catch problems before they become outages.
  • Security: Logs show unauthorized access attempts, failed logins, and suspicious activity.
  • Auditing: Need to know when something happened? Logs have timestamps.

Most container images - whether it's Nextcloud, Jellyfin, Pi-hole, or Portainer - write useful information to stdout and stderr. The docker logs command is how you read it.

Viewing Docker Logs - The Basics

Let's start with the most fundamental command. If you only learn one thing from this article, make it this:

docker logs <container_name_or_id>

That's it. Replace <container_name_or_id> with the actual name or ID of your container. Not sure what your containers are called? Run:

docker ps --format "table {{.Names}}\t{{.Status}}"

This shows you a clean list of running containers with their names and status.

Following Logs in Real-Time

Sometimes you need to watch what a container is doing right now - maybe you're debugging a startup issue or watching a migration. The -f (or --follow) flag streams the logs in real-time, just like tail -f works on regular files:

docker logs -f <container_name>

This will keep printing new log entries as they appear. Press Ctrl+C to stop following - it won't stop the container, just the log stream.

Showing Only Recent Logs

When a container has been running for months, docker logs can return thousands of lines. Use --tail to limit the output:

# Show only the last 50 lines
docker logs --tail 50 <container_name>

# Show only the last 10 lines
docker logs --tail 10 <container_name>

This is the command I use most often. It gives you the recent context without drowning in output.

Adding Timestamps

By default, Docker logs don't include timestamps. Add the -t (or --timestamps) flag to see exactly when each entry was written:

docker logs -t --tail 20 <container_name>

Timestamps are invaluable when you're trying to figure out when a problem started. The format is ISO 8601 - for example: 2026-06-13T10:45:32.123456789Z.

Filtering by Time

Need to see what happened in the last hour? Or between two specific times? Docker has you covered:

# Logs from the last 30 minutes
docker logs --since 30m <container_name>

# Logs from the last 2 hours
docker logs --since 2h <container_name>

# Logs up until 1 hour ago
docker logs --until 1h <container_name>

# Logs from a specific timestamp
docker logs --since "2026-06-13T08:00:00" <container_name>

Supports relative durations (30m, 2h, 1d) and RFC 3339 timestamps. This combination is powerful for narrowing down exactly when an issue occurred.

Combining Flags

The real power comes from combining these flags. Here are the combinations I use daily:

# Follow the last 100 lines with timestamps
docker logs -f -t --tail 100 <container_name>

# Last hour of logs, last 200 lines, with timestamps
docker logs --since 1h --tail 200 -t <container_name>

# Search logs for errors (using grep)
docker logs <container_name> 2>&1 | grep -i "error\|fail\|exception"

That last one is a lifesaver. Pipe the output through grep to find error messages, failures, or exceptions in the log stream.

Understanding Docker's Logging Drivers

Here's something most beginner guides skip: Docker doesn't just store logs in a text file. It uses something called a logging driver - a pluggable system that determines how and where logs are stored.

The default logging driver is json-file, which stores logs as JSON files on your host machine. But Docker supports several others, each with different trade-offs.

Logging Driver Comparison

Driver Stores Logs Supports docker logs Log Rotation Best For
json-file JSON files on disk Yes Manual (must configure) Development, simple setups
local Compressed binary files Yes Built-in (auto-rotation) Production homelabs
syslog Syslog server No Handled by syslog Existing syslog infrastructure
journald systemd journal No (use journalctl) Handled by journald Systemd-based hosts
fluentd Fluentd collector No Handled by Fluentd Centralized logging setups

For most homelab setups, I recommend starting with json-file with rotation configured, then moving to local once you're comfortable. The local driver uses less disk space and has built-in rotation - it's the best of both worlds for small-to-medium homelabs.

Here's a quick reference from the Docker logging configuration docs if you want to explore all available drivers.

Setting Up Log Rotation (The Most Important Step)

This is the section that will save your homelab. By default, Docker's json-file driver does not rotate logs. That means every log line your containers write gets stored on disk forever - or until your disk runs out.

With 15-20 containers running, it's easy to accumulate gigabytes of log data without realizing it. Here's how to prevent that.

Global Log Rotation via daemon.json

Edit (or create) the Docker daemon configuration file:

sudo nano /etc/docker/daemon.json

Add the following configuration:

{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  }
}

What this does:

  • max-size: "10m" - Each log file is capped at 10 megabytes.
  • max-file: "3" - Docker keeps 3 rotated log files, deleting the oldest when the limit is reached.

After saving, restart Docker:

sudo systemctl restart docker

Important: This applies to newly created containers only. Existing containers keep their old logging settings until they're recreated. If you have containers you can't recreate right away, you can set per-container overrides (see the next section).

Per-Container Log Rotation in Docker Compose

For containers managed by Docker Compose, you can set logging options directly in your docker-compose.yml:

services:
  jellyfin:
    image: jellyfin/jellyfin:latest
    container_name: jellyfin
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"
    volumes:
      - ./config:/config
      - /media:/media

Here's a more complete example with a logging section you can reuse across services using Docker Compose best practices:

services:
  portainer:
    image: portainer/portainer-ce:latest
    container_name: portainer
    logging:
      driver: json-file
      options:
        max-size: "5m"
        max-file: "2"
    ports:
      - "9443:9443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - portainer_data:/data

  homepage:
    image: ghcr.io/gethomepage/homepage:latest
    container_name: homepage
    logging:
      driver: json-file
      options:
        max-size: "5m"
        max-file: "2"
    ports:
      - "3000:3000"
    volumes:
      - ./config:/app/config

volumes:
  portainer_data:

You can also use YAML anchors to avoid repeating the logging config in every service:

x-logging: &default-logging
  driver: json-file
  options:
    max-size: "10m"
    max-file: "3"

services:
  jellyfin:
    image: jellyfin/jellyfin:latest
    logging: *default-logging

  portainer:
    image: portainer/portainer-ce:latest
    logging: *default-logging

This keeps your Compose files clean while ensuring every container has proper log rotation.

Using the Local Logging Driver

If you want more efficient logging with built-in rotation, switch to the local driver. It compresses logs automatically and uses less disk space:

{
  "log-driver": "local",
  "log-opts": {
    "max-size": "10m",
    "max-file": "5"
  }
}

The local driver stores logs in a binary format, which means smaller files and faster writes. The trade-off is that you can't just cat the log files directly - you need to use docker logs to read them. For most homelab users, this is perfectly fine.

For more details on log driver options, check the Docker json-file driver docs and the Docker local driver docs.

Emergency: What to Do When Docker Logs Fill Your Disk

If you're reading this section with a full disk and a panicking heart - take a breath. Here's the fix.

Step 1: Find Which Containers Are Using the Most Log Space

# Check overall Docker disk usage
docker system df

# Check individual container sizes
docker ps -a --format "table {{.Names}}\t{{.Size}}" | sort -k2 -h

The Size column shows how much space each container is using, including its logs. You can also check the actual log file sizes:

# Find all Docker log files and their sizes
sudo du -sh /var/lib/docker/containers/*/

Step 2: Truncate the Large Log Files

You don't need to stop containers to fix this. Truncate the log file to zero bytes:

# Truncate a specific container's logs
truncate -s 0 $(docker inspect --format='{{.LogPath}}' <container_name>)

# Or truncate ALL container logs at once
sudo truncate -s 0 /var/lib/docker/containers/*/*-json.log

This immediately frees the disk space. The container keeps running and continues logging - you've just cleared the backlog.

Step 3: Apply Log Rotation to Prevent Recurrence

Now configure log rotation so this doesn't happen again (see the "Setting Up Log Rotation" section above). After configuring daemon.json, restart Docker and recreate the problematic containers:

# Restart Docker
sudo systemctl restart docker

# Recreate containers to apply new logging settings
docker compose down && docker compose up -d

This is one of the most common homelab problems, and it's completely preventable with 5 minutes of configuration.

Understanding Where Docker Stores Logs

It helps to know exactly where Docker keeps log files on your system. With the default json-file driver, logs live in:

/var/lib/docker/containers/<container_id>/<container_id>-json.log

The <container_id> is the full 64-character container ID. You can find it with:

# Get the log path for a specific container
docker inspect --format='{{.LogPath}}' <container_name>

# Example output:
# /var/lib/docker/containers/abc123def456.../abc123def456...-json.log

These files are standard JSON, one entry per line. Each line contains a timestamp and the log message. You can read them directly with cat or process them with jq:

# Read raw log file
sudo cat /var/lib/docker/containers/abc123.../abc123...-json.log

# Parse with jq to see just the log messages
sudo cat /var/lib/docker/containers/abc123.../abc123...-json.log | jq -r '.log'

Knowing this path is useful when you need to check disk usage or manually clean up logs without using Docker commands.

Docker Logs After Container Restart

A common question: do logs survive a container restart?

Yes - Docker logs persist across container restarts. The log file lives on the host filesystem, not inside the container. When you docker restart a container, the same log file continues to accumulate entries.

No - logs do NOT survive container removal. When you run docker rm, the log file is deleted. If you need logs to persist after container removal, you need to either:

  1. Copy them before removal: docker logs <container> > backup.log
  2. Use a centralized logging system (covered in the next section)
  3. Mount the log directory to a volume

This is why centralized logging becomes important as your homelab grows - you want a copy of your logs somewhere other than the container's ephemeral storage.

Centralized Logging for Your Homelab: Promtail + Loki + Grafana

When you're running more than about 10-15 containers, checking docker logs on each one becomes impractical. You need a centralized place to search, filter, and visualize all your logs at once.

The homelab-friendly stack for this is Promtail + Loki + Grafana. Think of it as:

  • Promtail - the collector that grabs logs from all your containers
  • Loki - the storage engine that indexes and stores them
  • Grafana - the dashboard where you search and visualize everything

If you've used Prometheus and Grafana for monitoring container health, this stack follows the same pattern - just for logs instead of metrics.

Docker Compose Setup

Here's a complete docker-compose.yml for the logging stack. Create a new directory for it:

mkdir -p ~/logging-stack
cd ~/logging-stack
nano docker-compose.yml

Paste this configuration:

services:
  loki:
    image: grafana/loki:3.4.2
    container_name: loki
    ports:
      - "3100:3100"
    volumes:
      - ./loki-config.yml:/etc/loki/local-config.yaml
      - loki_data:/loki
    logging:
      driver: json-file
      options:
        max-size: "5m"
        max-file: "2"

  promtail:
    image: grafana/promtail:3.4.2
    container_name: promtail
    volumes:
      - ./promtail-config.yml:/etc/promtail/config.yml
      - /var/lib/docker/containers:/var/lib/docker/containers:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro
    logging:
      driver: json-file
      options:
        max-size: "5m"
        max-file: "2"

  grafana:
    image: grafana/grafana:11.6.0
    container_name: grafana-logging
    ports:
      - "3001:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=changeme
    volumes:
      - grafana_data:/var/lib/grafana
    logging:
      driver: json-file
      options:
        max-size: "5m"
        max-file: "2"

volumes:
  loki_data:
  grafana_data:

Notice that even the logging stack containers have log rotation configured - practice what you preach.

Loki Configuration

Create the Loki config file:

nano loki-config.yml
auth_enabled: false

server:
  http_listen_port: 3100

common:
  path_prefix: /loki
  storage:
    filesystem:
      chunks_directory: /loki/chunks
      rules_directory: /loki/rules
  replication_factor: 1
  ring:
    kvstore:
      store: inmemory

schema_config:
  configs:
    - from: "2024-01-01"
      store: tsdb
      object_store: filesystem
      schema: v13
      index:
        prefix: index_
        period: 24h

limits_config:
  reject_old_samples: true
  reject_old_samples_max_age: 168h

Promtail Configuration

Create the Promtail config that scrapes Docker container logs:

nano promtail-config.yml
server:
  http_listen_port: 9080

positions:
  filename: /tmp/positions.yaml

clients:
  - url: http://loki:3100/loki/api/v1/push

scrape_configs:
  - job_name: docker
    docker_sd_configs:
      - host: unix:///var/run/docker.sock
        refresh_interval: 5s
    relabel_configs:
      - source_labels: ['__meta_docker_container_name']
        regex: '/(.*)'
        target_label: 'container'
      - source_labels: ['__meta_docker_container_log_stream']
        target_label: 'logstream'
      - source_labels: ['__meta_docker_container_label_com_docker_compose_service']
        target_label: 'service'

This configuration automatically discovers all running Docker containers and starts collecting their logs. When you add or remove containers, Promtail picks up the changes within 5 seconds.

Start the Stack

docker compose up -d

After a minute or two, open Grafana at http://your-server-ip:3001 (login with admin / changeme - change this immediately).

Add Loki as a data source:

  1. Go to Settings > Data Sources > Add data source
  2. Select Loki
  3. Set the URL to http://loki:3100
  4. Click Save & Test

Now go to Explore and run a query like:

{container=~".+"}

You should see logs from all your containers in one searchable view. From here, you can filter by container name, search for error messages, and set up alerts.

Common Docker Logging Mistakes

After helping dozens of homelabbers with logging issues, here are the mistakes I see most often:

  1. Not configuring log rotation from day one. Every new homelabber learns this lesson the hard way. Set up daemon.json before you create your first container.
  2. Using Docker Compose without logging options. If you're writing docker-compose.yml files, always include the logging section. It takes 4 lines and saves you from disk emergencies.
  3. Restarting containers to "clear" logs. Restarting doesn't clear logs - the log file persists. Use truncate -s 0 instead, or configure proper rotation.
  4. Ignoring stderr output. Many people only look at stdout logs and miss error messages written to stderr. When using grep to search logs, always include both: docker logs <container> 2>&1 | grep error.
  5. Centralized logging too early (or too late). You don't need Promtail + Loki for 3 containers, but you absolutely need it for 30. The sweet spot is around 10-15 containers when manual checking becomes tedious.
  6. Not monitoring disk usage. Even with log rotation configured, 3 containers writing 10MB logs with 3 files each = 90MB. With 30 containers, that's 900MB - not huge, but worth monitoring. Set up a simple disk usage alert in Grafana or Uptime Kuma.

Practical Docker Log Commands Cheat Sheet

Here's a quick reference you can bookmark:

# View all logs
docker logs <container>

# Follow logs in real-time
docker logs -f <container>

# Last 50 lines
docker logs --tail 50 <container>

# Last hour with timestamps
docker logs --since 1h -t <container>

# Follow last 100 lines with timestamps
docker logs -f -t --tail 100 <container>

# Search for errors
docker logs <container> 2>&1 | grep -i error

# Check log file size
docker inspect --format='{{.LogPath}}' <container> | xargs du -sh

# Truncate a container's logs
truncate -s 0 $(docker inspect --format='{{.LogPath}}' <container>)

# Truncate ALL container logs
sudo truncate -s 0 /var/lib/docker/containers/*/*-json.log

# Check which containers use the most disk
docker system df -v

What to Learn Next

Now that you understand Docker logging basics, here's a natural progression for your homelab journey:

  1. Docker health checks - Learn how containers report their health status, and use it to automate restarts when something goes wrong.
  2. Docker backup strategies - Logs tell you what went wrong. Backups let you recover. Learn the backup patterns that actually work.
  3. Docker volumes vs bind mounts - Understand where your container data actually lives, including log files.
  4. Docker for homelabs - A complete beginner's guide if you want to strengthen your Docker fundamentals.
  5. Homelab backups - Beyond Docker-specific backups, learn how to protect your entire homelab infrastructure.

FAQ

Where are Docker container logs stored on disk?

With the default json-file logging driver, Docker stores container logs at /var/lib/docker/containers/<container_id>/<container_id>-json.log. You can find the exact path for any container with docker inspect --format='{{.LogPath}}' <container_name>.

How do I stop Docker logs from filling up my disk?

Configure log rotation in /etc/docker/daemon.json by setting "log-opts": {"max-size": "10m", "max-file": "3"}. This limits each log file to 10MB and keeps only 3 rotated files. For existing large logs, use truncate -s 0 on the log file to immediately free space.

Do Docker logs survive a container restart?

Yes, Docker logs persist across restarts because the log file lives on the host filesystem, not inside the container. However, logs are deleted when you remove a container with docker rm. To preserve logs, use a centralized logging system or copy them before removal.

What's the difference between the json-file and local logging drivers?

The json-file driver stores logs as plain JSON files - human-readable but larger. The local driver stores compressed binary logs - smaller and faster, but you need docker logs to read them. The local driver also has built-in rotation, making it the better choice for most homelabs.

How do I search Docker logs for specific errors?

Pipe the output through grep: docker logs <container> 2>&1 | grep -i "error". The 2>&1 part combines both stdout and stderr so you don't miss error messages. For more advanced searching across multiple containers, set up a centralized logging stack with Grafana and Loki.

Beelink SER5 MAX Mini PC

Beelink SER5 MAX Mini PC - AMD Ryzen 7 5800H

A dedicated logging and monitoring server for your homelab. Run Grafana, Loki, and Promtail without impacting your main workloads.

$489.00

View on Amazon
As an Amazon Associate, we earn from qualifying purchases.
Crucial 32GB DDR5 RAM Kit

Crucial 32GB DDR5 RAM Kit (2x16GB)

More RAM means your logging stack can handle more containers without swapping. Loki indexes are memory-hungry - 32GB gives you breathing room.

$79.99

View on Amazon
As an Amazon Associate, we earn from qualifying purchases.
Samsung 970 EVO Plus 1TB NVMe SSD

Samsung 970 EVO Plus 1TB NVMe SSD

Fast local storage for Docker log files and Loki's chunk storage. NVMe speeds mean log writes don't slow down your containers.

$89.99

View on Amazon
As an Amazon Associate, we earn from qualifying purchases.