---
title: "How to Redirect Docker Logs to a Single File"
description: "Learn how to redirect Docker logs to a single file using docker logs commands, json-file driver configuration, and the local driver. Practical guide with code examples."
date: 2026-07-09
categories: ["self-hosting"]
tags: ["docker","logging","devops"]
---

Docker stores each container's logs in separate files under `/var/lib/docker/containers/`. When you're running multiple containers, searching through scattered log files gets old fast. This guide shows three ways to redirect Docker logs to a single file — from a quick one-liner to daemon-wide configuration.

**Other Docker guides you might find useful:**

- [Add Users to a Docker Container](https://www.bitdoze.com/add-users-to-docker-container/)
- [Copy Multiple Files in One Layer Using a Dockerfile](https://www.bitdoze.com/copy-multiple-files-in-one-layer-using-a-dockerfile/)
- [Install Docker & Docker-compose for Ubuntu ARM](https://www.bitdoze.com/install-docker-ubuntu-arm/)
- [Environment Variables ARG and ENV in Docker](https://www.bitdoze.com/docker-env-vars/)

## Method 1: One-off redirect with docker logs

The simplest way to dump container logs to a file:

```bash
docker logs my-container > container.log 2>&1
```

The `2>&1` part matters. Without it, you only capture stdout. stderr gets lost. This command merges both streams into one file.

To follow logs in real-time and write them continuously:

```bash
docker logs -f my-container > container.log 2>&1 &
```

The `-f` flag follows new output. The `&` runs it in the background. This is useful for debugging but has limitations — the process dies when your shell session ends.

To capture logs from multiple containers into one file:

```bash
for container in $(docker ps --format '{{.Names}}'); do
  echo "=== $container ===" >> all-logs.log
  docker logs "$container" >> all-logs.log 2>&1
done
```

**When to use this method:** Quick debugging, one-off log exports, or when you need a snapshot of what's happening right now.

## Method 2: Configure json-file driver with rotation

Docker uses the `json-file` logging driver by default. It writes logs as JSON to `/var/lib/docker/containers/<id>/<id>-json.log`. The catch: no rotation is enabled by default. A chatty container will fill your disk.

### Per-container configuration

Set the logging driver when starting a container:

```bash
docker run -d \
  --name my-app \
  --log-driver json-file \
  --log-opt max-size=10m \
  --log-opt max-file=3 \
  my-app:latest
```

- `max-size=10m` — caps each log file at 10 MB
- `max-file=3` — keeps 3 rotated files (oldest gets deleted)

With these settings, the container uses at most 30 MB for logs.

### Daemon-wide configuration

To apply rotation to every new container, edit `/etc/docker/daemon.json`:

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

Restart Docker for the changes to take effect:

```bash
sudo systemctl restart docker
```

**Important:** Existing containers don't pick up the new defaults. You need to recreate them. Also, all values in `daemon.json` must be strings — `"max-file": 3` (without quotes) will break the daemon.

### Verify the configuration

Check a container's current logging driver:

```bash
docker inspect --format='{{.HostConfig.LogConfig}}' my-app
```

Find the log file path:

```bash
docker inspect --format='{{.LogPath}}' my-app
```

## Method 3: Use the local driver (recommended for production)

The `local` driver is Docker's recommended replacement for `json-file` in most situations. It uses a more efficient binary format, compresses rotated files automatically, and has sensible defaults (20 MB per file, 5 files kept).

```json
{
  "log-driver": "local"
}
```

Or per-container:

```bash
docker run -d \
  --name my-app \
  --log-driver local \
  my-app:latest
```

The `docker logs` command works the same way with both drivers.

**Why json-file is still the default:** Docker can't switch without breaking tools that depend on the json-file layout — Kubernetes being the main one. If you're not running Kubernetes, use `local`.

### Docker Compose configuration

For Compose stacks, use the `logging` key:

```yaml
services:
  api:
    image: my-app:latest
    logging:
      driver: local
  
  worker:
    image: my-worker:latest
    logging:
      driver: json-file
      options:
        max-size: "20m"
        max-file: "5"
```

To apply the same logging config across all services with a YAML anchor:

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

services:
  api:
    image: my-app:latest
    logging: *default-logging
  
  worker:
    image: my-worker:latest
    logging: *default-logging
```

## Where Docker stores logs by default

Before redirecting logs, it helps to understand the default behavior:

- Docker captures stdout and stderr from every container
- Each container gets its own log file under `/var/lib/docker/containers/`
- Logs are stored in JSON format with timestamps and stream type
- No rotation is enabled by default (this is the main problem)

Find the log file for a specific container:

```bash
docker inspect --format='{{.LogPath}}' my-container
```

Output looks like:

```
/var/lib/docker/containers/a4f8c9e1.../a4f8c9e1...-json.log
```

Each line is a JSON object:

```json
{"log":"Listening on port 8080\n","stream":"stdout","time":"2023-07-03T10:14:02.123456789Z"}
```

**Don't use external log rotation tools** (like `logrotate`) on Docker's internal log files. Docker assumes exclusive access. External truncation can corrupt log state or prevent containers from being removed.

## Forwarding logs to a central system

For production setups with multiple hosts, consider forwarding logs to a central system:

### Syslog driver

```bash
docker run -d \
  --log-driver syslog \
  --log-opt syslog-address=tcp://logs.example.com:514 \
  --log-opt tag="{{.Name}}" \
  my-app:latest
```

### Fluentd driver

```bash
docker run -d \
  --log-driver fluentd \
  --log-opt fluentd-address=localhost:24224 \
  --log-opt tag="docker.{{.Name}}" \
  my-app:latest
```

Since Docker 20.10, remote drivers (syslog, fluentd, splunk) automatically maintain a local cache alongside forwarding. `docker logs` still works. The cache uses the `local` driver internally with 5 files of 20 MB each.

Other available drivers: `journald`, `gelf` (Graylog), `awslogs` (CloudWatch), `splunk`.

## Common issues

**Container logs filling disk:** Enable rotation with `max-size` and `max-file`. This is the most common Docker disk issue. Without rotation, json-file has no upper bound.

**`docker logs` not showing output after changing driver:** Make sure the container was created after the daemon configuration change. Existing containers keep their original settings.

**Daemon won't start after editing daemon.json:** Validate the JSON first:

```bash
sudo dockerd --validate --config-file /etc/docker/daemon.json
```

If the daemon is already down, check `journalctl -u docker` for error details.

**Want to disable local caching for remote drivers:** Add `"cache-disabled": "true"` to `log-opts`.

## Which method should you use?

| Method | Best for | Persistence |
|--------|----------|-------------|
| `docker logs > file` | Quick debugging, one-off exports | No (runs in shell) |
| `json-file` with rotation | Single-host setups, Kubernetes | Yes (managed by Docker) |
| `local` driver | Single-host production setups | Yes (more efficient) |
| `syslog`/`fluentd` | Multi-host, centralized logging | Yes (external system) |

For most self-hosted setups, the `local` driver with default settings is the right choice. It handles rotation automatically and uses less disk than `json-file`.