What this check reads
It reads /etc/docker/daemon.json and greps two keys out of it, "log-driver" and "max-size". The driver decides the branch, and an absent "log-driver" is treated as json-file, which is Docker's own default. On json-file the presence of a "max-size" value anywhere in the file is the whole test: with one the check PASSes, without one it WARNs. Any other driver value PASSes outright, on the assumption that the driver handles its own rotation.
When it applies
Runs whenever the docker binary exists; no daemon access or root needed, since it only reads /etc/docker/daemon.json. It inspects the daemon-wide default log driver and max-size. With the default json-file driver and no max-size it WARNs; with any other driver it PASSes on the assumption that driver handles its own retention.
Known accuracy issue: per-container settings are invisible. A host that caps logs on every container with --log-opt max-size=10m (or a Compose logging: block) still gets a WARN, and conversely a daemon default of max-size does not prove a container has not overridden it. This is the known issue flagged at the top of this page.
What each result means
| Result | When | What it means |
|---|---|---|
| SKIP | Docker not installed | Nothing was checked. With no docker binary on the host, no log driver setting was read at all. |
| PASS | `json-file` with `max-size` set | The daemon default caps container logs and rotates them, so they cannot grow without bound. |
| WARN | `json-file` without `max-size` | The daemon default puts no ceiling on container logs, so one chatty container can fill the disk. Per-container caps are invisible to this check. |
| PASS | Other driver (`local`, `journald`, `syslog`, ...) | Logging is handed to a different driver and the check assumes that driver enforces its own retention. Confirm that on the receiving end. |
Why it matters
Docker's default json-file driver has no size limit. A chatty or compromised container fills the disk, which stops the daemon, databases and SSH logins on the host (availability, and a denial-of-service vector). Docker docs recommend the local driver (which rotates by default) or explicit max-size/max-file. CIS Docker 2.13 covers centralised logging.
Why it fails, and when it is wrong
- Per-container
--log-opt max-size=10m(Composelogging: options:) is not read; hosts that cap logs per service get a false WARN. Confirm withdocker inspect -f '{{.HostConfig.LogConfig}}' <name>. - Changing
daemon.jsononly affects new containers; existing ones keep their original LogConfig until recreated. daemon.jsonunreadable → both greps are empty → WARN even if configured.
How to fix it
Set a capped default in daemon.json
Merge this into the keys already in /etc/docker/daemon.json. The local driver rotates on its own; to keep json-file, use the same log-opts with "log-driver": "json-file". Both values must be strings.
{
"log-driver": "local",
"log-opts": { "max-size": "20m", "max-file": "5" }
}Validate and restart Docker
An invalid daemon.json stops the daemon from starting, so check it parses first.
python3 -m json.tool /etc/docker/daemon.json >/dev/null && sudo systemctl restart dockerRecreate the containers
Existing containers keep the log configuration they were created with, so the new default only applies once they are recreated. Shipping logs to journald, syslog or Loki instead is the other route.
docker compose up -d --force-recreateVerify the fix
# The daemon default (what the check reads):
sudo cat /etc/docker/daemon.json
docker info --format 'driver={{.LoggingDriver}}'
# What each running container actually uses - the part the check cannot see:
docker inspect -f '{{.Name}} {{.HostConfig.LogConfig.Type}} {{.HostConfig.LogConfig.Config}}' $(docker ps -q)
# expected: a max-size on every json-file container
# And the ground truth - how big the log files actually are:
# (sh -c, because the directory is root-only and the glob must expand as root)
sudo sh -c 'du -sh /var/lib/docker/containers/*/*-json.log' | sort -h | tail -5Debugging
The known false positive above. Confirm with the docker inspect command; if every container carries a max-size, record it. Setting the daemon-wide default as well is the cleanest resolution, and it protects containers created later that forget the flag.
The check greps for "max-size" anywhere in the file, so a syntax problem is the likely cause. max-size belongs inside log-opts, and its value must be a string ("10m", not 10m). Validate with python3 -m json.tool /etc/docker/daemon.json; an invalid daemon.json also stops the daemon from starting.
Log configuration is fixed at container creation. Recreate them (docker compose up -d --force-recreate) for the new default to apply.
The check assumes journald, syslog, local and remote drivers manage their own retention. Verify that assumption on the receiving end: journald obeys SystemMaxUse in journald.conf, and a remote collector has its own policy. The local driver does rotate by default; json-file does not.
Truncate safely with sudo sh -c 'truncate -s 0 /var/lib/docker/containers/*/*-json.log' (deleting the file leaves the daemon writing to a removed inode), then fix the default and recreate the containers. The sh -c matters: the containers directory is readable only by root, so a plain sudo truncate with the glob fails because your own shell cannot expand it.
Sources
- Docker: Configure logging drivers (json-file defaults, local driver)
- Docker: JSON File logging driver options (
max-size,max-file) - Docker: Local File logging driver
- Docker Compose: logging
How the script reads this
Next
Re-run the Daemon & Socket audit after applying the fix and confirm this check moves to PASS. CtrlOps runs all 8 checks over your existing SSH connection and scores the result, so the change is visible without reading another config file.
All 8 Daemon & Socket fixes