What this check reads
It reads the running dockerd command line out of ps -eo args and takes the value of --log-level from it. If no flag is set there it falls back to the "log-level" key in /etc/docker/daemon.json, so the command line always wins, exactly as it does for the daemon itself. The comparison is a literal string match on debug: only that value WARNs, and every other value PASSes, including an unset one, which is reported as info (default).
When it applies
Runs whenever the docker binary exists; it needs neither daemon access nor root, reading --log-level from the running dockerd command line first and falling back to "log-level" in /etc/docker/daemon.json (which is world-readable on a default install, so the check needs no root). Only the exact value debug produces a finding; trace, an invalid value, or no value at all all report as PASS, with an unset value shown as "info (default)". It judges the daemon's log verbosity only - per-container logging and the log driver are separate concerns, covered by the Container Log Limits check.
What each result means
| Result | When | What it means |
|---|---|---|
| SKIP | Docker not installed | Nothing was checked. With no docker binary present, the daemon's log level was never read. |
| WARN | `debug` | The daemon is writing debug records, which include full API request bodies and can carry registry credentials and build arguments into the journal. |
| PASS | `info` (default) or anything else | No debug level was found on the command line or in daemon.json. Note that trace, which is more verbose still, also lands here. |
Why it matters
Debug logging records API request bodies, environment variables passed at docker run, registry auth flows and more into the journal, where a wider set of users may read them. It also degrades performance. CIS Docker 2.3 "Ensure the logging level is set to 'info'".
Why it fails, and when it is wrong
- Left over from troubleshooting a daemon issue.
- If
daemon.jsonis not readable by the audit user, the JSON path is silently empty and the command-line path is used; adebugset only in an unreadable daemon.json would be missed. -D/--debugon the command line and"debug": trueindaemon.jsonboth put the daemon at debug level, and neither is matched: the check only reads--log-leveland"log-level".
How to fix it
Find where debug is set
The command line wins over the file, for the daemon and for the audit, so check both.
systemctl cat docker | grep -n ExecStart
sudo grep -nE '"(log-level|debug)"' /etc/docker/daemon.jsonSet it to info
Set the level in daemon.json, merged with whatever keys the file already has, and delete any "debug": true. Remove --log-level debug or -D from the drop-in with sudo systemctl edit docker.
{ "log-level": "info" }Restart the daemon
sudo systemctl daemon-reload
sudo systemctl restart dockerThe restart stops every running container, and only those with an always or unless-stopped restart policy come back on their own, unless "live-restore": true is set in daemon.json.
Verify the fix
ps -eo args | grep '[d]ockerd' # expected: no --log-level debug
sudo grep -n log-level /etc/docker/daemon.json # expected: "info", or absent
docker info --format '{{.LoggingDriver}}'
# Confirm the daemon is not emitting debug records:
sudo journalctl -u docker --since '-1h' | grep -c 'level=debug'
# expected: 0Debugging
Usually left over from troubleshooting. Remove --log-level debug from the systemd drop-in (systemctl cat docker) or set "log-level": "info" in daemon.json, then systemctl restart docker. Debug output includes full API request bodies, which can contain registry credentials and build arguments.
The command line wins: the check reads it first, and so does the daemon. systemctl cat docker | grep ExecStart shows whether a flag is overriding your file.
This check only looks at the daemon's own verbosity. Runaway container logs are the far more common cause of a full disk; see the Container Log Limits check.
A genuine gap: only the literal string debug is matched, so the even more verbose trace slips through. Check by hand with the ps/grep commands above.
Log level is read at daemon start, so the change needs systemctl restart docker. That stops every running container, and only those with an always or unless-stopped restart policy come back on their own, unless "live-restore": true is set in daemon.json, which keeps containers running while the daemon restarts. Plan the restart.
Sources
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