Why this matters
A classic 3am page: the root disk of a Docker host is at 100%. Someone runs Docker's own disk report and it "looks fine". The culprit is a container's log file, which by default grows forever and which that report does not count.
What you need to know already:
dfanddufor finding space (4.19); deleted-but-open files (4.21); log rotation and journald limits (2.30); JSON andjq(7.11); restarting a systemd service (2.5).
Where docker logs come from
Docker hands a container's output to a logging driver - the plug-in that decides where log lines go. The default, json-file, writes every line PID 1 prints to a file on the host, one JSON object per line:
$ docker inspect -f '{{.LogPath}}' web
/var/lib/docker/containers/3f4e1a2b.../3f4e1a2b...-json.log
$ sudo tail -2 "$(docker inspect -f '{{.LogPath}}' web)"
{"log":"2026/09/23 10:12:01 [notice] 1#1: start worker processes\n","stream":"stderr","time":"2026-09-23T10:12:01.458123000Z"}
{"log":"172.17.0.1 - - [23/Sep/2026:10:12:09 +0000] \"GET / HTTP/1.1\" 200 615\n","stream":"stdout","time":"2026-09-23T10:12:09.001234000Z"}
Each object: log = the line, stream = stdout or stderr, time = when. docker logs reads that file. By default it is never rotated (rotation = start a new file at a size limit and delete the oldest, like journald's limits in 2.30). A chatty container writes until the disk is full.
# on a box where one container has logged for weeks (the glob has to expand as root, hence sh -c)
sudo sh -c 'du -sh /var/lib/docker/containers/*' | sort -h | tail -3
12K /var/lib/docker/containers/8a7b...
640K /var/lib/docker/containers/3f4e...
9.1G /var/lib/docker/containers/c2d4...
(df says / is full; du -sh sizes each directory; sort -h sorts human sizes. The directory name is the container's full ID.)
Rotation: per container, or as the default
docker run --log-opt max-size=10m --log-opt max-file=3 ...
--log-opt passes an option to the driver: keep at most three files of 10MB (-json.log, -json.log.1, -json.log.2). For every container on the host, set it as the daemon's default in /etc/docker/daemon.json, dockerd's config file:
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
sudo systemctl restart docker
dockerd reads the file only when it starts, hence the restart. Two traps:
- Existing containers keep the settings they were created with. The daemon default only applies to containers created after the restart. The chatty container must be recreated (
docker rm+docker run) to get rotation.docker inspect -f '{{json .HostConfig.LogConfig}}' NAMEshows what a container has. - Invalid JSON stops dockerd from starting. One trailing comma and every container on the host is down. Validate first (
jq . /etc/docker/daemon.json- it errors on bad JSON) and readjournalctl -u docker -n 20if it fails.
The local driver ("log-driver": "local") is a good alternative default: compressed, rotated automatically, and docker logs still works.
Other drivers
journald lines go to the systemd journal (journalctl CONTAINER_NAME=web)
syslog/gelf/fluentd/awslogs/splunk ship to a central log system
none nothing is kept
With none (and some shipping drivers), docker logs fails with configured logging driver does not support reading.
Disk usage in general
$ docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 14 6 4.21GB 2.64GB (62%)
Containers 9 4 118MB 22.1MB (18%)
Local Volumes 5 2 1.04GB 312MB (30%)
Build Cache 61 0 1.83GB 1.83GB
docker system df is Docker's own disk report. Columns: TOTAL how many, ACTIVE in use by a container, SIZE on disk, RECLAIMABLE what cleanup could free. Note what is not there: container log files. That is why a log-filled disk surprises people who "checked Docker".
Cleanup, from gentle to brutal (prune = delete everything unused of that kind):
docker container prune stopped containers
docker image prune dangling images: untagged leftovers, <none>:<none>
docker image prune -a every image not used by a container
docker builder prune the build cache
docker volume prune unused anonymous volumes (-a: named too - data!)
docker system prune containers + networks + dangling images + build cache
docker system prune -a --volumes the lot
None of these shrink a running container's log. Rotation does. (Deleting the log file with rm does not free space either - dockerd keeps it open, the deleted-but-open trap from 4.21.)
What you can now do
- find which container's log is filling a disk
- set log rotation for every new container, and apply it to existing ones
- clean up Docker disk usage without touching volume data