Docker never deletes a container’s log. Not by default, not ever.
The default json-file driver has no size limit. A chatty container writes until the partition is full. A full /var/lib/docker then takes every container on the host down with it, not just the noisy one.
Docker’s own documentation says so, and recommends a different driver. Most installs never change it.
docker system df will not help. It counts images, volumes and each container’s writable layer. Logs live outside all three, so it never counts them.
Go to the files:
sudo du -h /var/lib/docker/containers/*/*-json.log | sort -rh | head
That gives you IDs. Turn the top one into a name:
docker ps -a --no-trunc --format '{{.ID}} {{.Names}}' | grep 4f1c
Use the first few characters of the ID from the du output. --no-trunc matters: the file name carries the full 64-character ID, and docker ps shortens it to 12 by default.
Rootless Docker, or a moved data-root? Replace /var/lib/docker with the output of docker info --format '{{.DockerRootDir}}'.
Add this to /etc/docker/daemon.json. Create the file if it does not exist:
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
Then restart the daemon:
sudo systemctl restart docker
Each container now keeps at most three files of 10 MB: the live one plus two rotated copies. That is 30 MB per container.
Three details in that file catch people.
The values are strings. "3", not 3. A number there is a configuration error, and the daemon refuses to start.
max-file does nothing alone. Rotation only happens when max-size is set. max-file without it keeps one file that grows forever.
The units are decimal. 10m is 10,000,000 bytes, not 10 MiB. Docker reads 10MiB the same way.
On Docker Desktop, edit the same JSON under Settings → Docker Engine instead of the file.
This is the trap. The new defaults apply to containers created after the restart. Every container that already exists keeps the settings it was created with. docker update cannot change logging options.
Check what a container is really using:
docker inspect --format '{{.HostConfig.LogConfig}}' my-container
{json-file map[max-file:3 max-size:10m]}
An empty map[] means no limit. The only fix is to recreate the container.
Recreating deletes the old log, because the log lives in the container’s own directory. Save it first if you need it:
docker logs my-container > my-container.log 2>&1
Keep the 2>&1. docker logs sends the container’s stderr to your stderr, and without it you save everything except the errors.
A single docker run:
docker run -d --log-opt max-size=10m --log-opt max-file=3 my-image
Compose, per service:
services:
api:
image: my-api
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
Compose notices the change. The next docker compose up -d recreates the container with the new settings, and the old log goes with the old container.
Per-container options override the daemon defaults. That is useful for the one service that logs every request and needs a bigger budget than the rest.
Docker’s documentation recommends the local driver “to prevent disk-exhaustion”. It rotates by default: five files of 20 MB, compressed. That is 100 MB per container with no settings at all.
{
"log-driver": "local"
}
docker logs works the same. Two things change.
The file is no longer readable. It is binary protobuf, with older files gzipped. LogPath comes back empty, too.
So anything that reads *-json.log directly stops working. That includes log shippers configured to tail /var/lib/docker/containers. Check before you switch.
The clean fix is to recreate the container with a limit. That frees the space and stops it coming back.
If you cannot recreate it this minute, truncating the live file frees the space:
sudo truncate -s 0 "$(docker inspect --format '{{.LogPath}}' my-container)"
Docker writes in append mode, so new lines go to the start of the empty file. You do not get a sparse file full of zeros.
It is still a stopgap. The daemon keeps its own running count of the file’s size and never re-reads it. After a truncate that count is wrong. With max-size set, the next rotation comes early. docker logs -f and --tail read from offsets that no longer exist, and may show nothing or misbehave. Docker’s documentation says no outside tool should touch these files.
Restart the container soon afterwards. That resets the count.
Deleting the file is worse than truncating it. Docker keeps it open, so the space is not freed. Fixing /var/log filling up covers deleted-but-open files and how to find them.
A 30 MB cap keeps the disk alive. It also means a busy container keeps a few hours of history. When an incident is a day old, the lines are gone.
That is fine if the logs live somewhere else too. Switch the driver to journald and each line goes to the systemd journal, which has its own size caps. docker logs keeps working. Ship the journal with the CL Agent, and the history outlives both the rotation and the container.
Sending Docker container logs to a central server covers the driver change and the trade-offs of each option.
Then hear about the next full disk before it happens. Central Logging’s host monitoring reports disk usage per host, and alerting on a disk running out of space covers the rule.
LogPath is often empty, and saving logs to a file.docker logs flags, Compose, and the three reasons it returns nothing.journald driver.💌 Get notified on new features and updates