# Docker logs and journald: choosing a logging driver

LLMS index: [llms.txt](/en/llms.txt)

---

When a container crashes, logs are the first thing you need to see. `docker logs` looks simple, but under the hood different logging drivers are at work, and the choice affects how logs are stored, rotated, and accessed. Here is what you should know before trusting the default.

## How docker logs works

The `docker logs <container>` command reads the container's stdout/stderr stream and outputs it to the terminal. Behind this sits a **logging driver** — a component that determines where the data actually goes. By default it is `json-file`: each container gets a JSON file on the host into which every output line is written.

> [!NOTE]
> `docker logs` does not read logs from inside the container directly — it queries the driver, which already stores the data in its own format and location.

The driver is configured at the Docker daemon level or per container. The choice affects log rotation, access via `journalctl`, and integration with centralized collection systems.

## Driver json-file (default)

`json-file` is the built-in driver with no external dependencies. Each container creates a file at `/var/lib/docker/containers/<container-id>/<container-id>-json.log`. The format is JSON lines: each entry contains `log`, `stream` (stdout or stderr), and `time`.

Rotation is controlled by two flags:

| Flag | Description |
|---|---|
| `max-size` | Maximum size of a single log file (e.g., `10m`) |
| `max-file` | Number of rotated files to retain |

Without these flags, log files grow without limits. In production this is a direct path to filling the disk.

```bash
docker run --log-driver json-file --log-opt max-size=10m --log-opt max-file=3 nginx
```

> [!WARNING]
> If `max-size` and `max-file` are not explicitly set, Docker does not limit log size. On a host with many containers this will result in unexpected disk exhaustion.

Logs can be read via `docker logs` or directly from the file path on the host, but the latter is not recommended because files may be held open by the daemon.

## Driver journald

`journald` sends container logs into the systemd journal. This means logs are accessible through `journalctl`, all of journald's rotation and compression mechanisms apply, and there are no separate JSON files growing on disk.

This requires `systemd` and the `systemd-journal-remote` package (on some distributions). The container must be started with the driver specified:

```bash
docker run --log-driver journald --log-opt tag={{.Name}} nginx
```

The `tag` flag sets the identifier in journald — without it the tag is an empty string and finding the right container becomes difficult. The template `{{.Name}}` substitutes the container name.

> [!TIP]
> Use `tag={{.Name}}` or `tag={{.ID}}` so that logs in journald are immediately tied to a specific container. Without a tag, filtering by `CONTAINER_NAME` does not work.

Reading logs:

```bash
journalctl -u docker --grep="nginx"
journalctl --user-console -t docker --since "1 hour ago"
```

More precisely, through journald filters:

```bash
journalctl -t docker -g "nginx" --since "2024-01-01"
```

Actual filtering depends on which metadata Docker passes into journald. Check `journalctl -o verbose` for a specific container to see what fields are available.

## Comparing json-file and journald

| Parameter | json-file | journald |
|---|---|---|
| Storage location | `/var/lib/docker/containers/...` | `/var/log/journal/` |
| Rotation | Via `--log-opt` | Via `journald.conf` |
| Search | `docker logs --since`, `grep` | `journalctl --grep`, `--since` |
| Dependencies | None | systemd |
| Centralization | Via `fluentd`, `gelf`, `awslogs` | Via `journalctl --remote` or forward |
| Compression | No (manual) | Yes, configurable in `journald.conf` |
| Access without Docker | Direct file access | Only via `journalctl` |

> [!WARNING]
> `journald` does not support all `log-opt` flags available for `json-file`. For example, `max-size` and `max-file` do not work — rotation is controlled by journald's own settings (`SystemMaxUse`, `SystemMaxFileSize`, etc.).

## Configuring the driver in daemon.json

The global setting goes in `/etc/docker/daemon.json`:

```json
{
  "log-driver": "journald",
  "log-opts": {
    "tag": "{{.Name}}"
  }
}
```

After changing it, restart Docker:

```bash
sudo systemctl restart docker
```

> [!NOTE]
> Changing the driver in `daemon.json` affects **all new containers**. Already running containers continue using their current driver until restarted.

Per-container override is possible via `--log-driver` and `--log-opt` at startup — this takes precedence over daemon settings.

To check the current driver for a specific container:

```bash
docker inspect --format='{{.HostConfig.LogConfig.Type}}' <container>
```

## Practical recommendations

For local development, `json-file` with explicit `max-size` and `max-file` is sufficient and simple to use. For production with dozens of containers on a single host, `journald` is preferable: a unified search space, built-in compression, and integration with `systemd` and monitoring.

> [!TIP]
> If you already use `systemd` to orchestrate containers (via `systemd` unit files or Podman), `journald` is the natural choice. Container logs and service logs end up in one place.

For centralized collection, both drivers support log forwarding through intermediate drivers (`fluentd`, `gelf`, `splunk`). But `journald` adds an extra step: first into journald, then the forwarder. For simple cases, direct `json-file` + `fluentd` may be a shorter path.

Monitor disk space regularly regardless of the driver. `journalctl --disk-usage` and `du -sh /var/lib/docker/containers/*/` are the minimum set for this.
