CertGrid CertGrid
Configuration·Docker

Docker Daemon Configuration

daemon.json changes every container on the host, and a malformed file stops Docker starting at all. Validate before you reload - and see the trap where reload applies a setting but removing it does not take it away.

Operations and Troubleshooting Guide 29 of 46 Advanced

Tested on the versions above. Every change here was applied to a real host and then reverted; the reverted state was verified. Your daemon may already have a daemon.json - back it up before editing.

One Docker host is all this guide needs. Nothing here depends on a second machine, and the hardware above is modest on purpose - a 2 core, 4 GB VM runs everything in this path.
Server NameIP AddressOSRolesCPURAMHDD
DOCKER01192.168.0.21Ubuntu 26.04 LTSDocker Host2 Core4 GB50 GB

Before you start

  1. See the settings before you change them

    docker info reports the daemon's effective configuration, which is what actually applies rather than what a file says. Record these before editing so you can tell what your change did and revert precisely.

    bash
    docker info --format "logging={{.LoggingDriver}} live-restore={{.LiveRestoreEnabled}} data-root={{.DockerRootDir}}"logging=json-file live-restore=false data-root=/var/lib/dockersudo test -f /etc/docker/daemon.json && sudo cp /etc/docker/daemon.json /tmp/daemon.bak || echo "no existing /etc/docker/daemon.json"

    Expected resultThe current effective settings, and a backup if a config file already existed.

    Success conditionYou have a baseline and a backup. On many hosts there is no daemon.json at all and every setting is a default.

  2. A malformed file stops Docker starting

    This is why the next step matters. dockerd --validate parses a config file and reports on it WITHOUT touching the running daemon - so you find out about a trailing comma now rather than when the service refuses to come back up.

    bash
    printf '%s' '{"log-driver": "json-file",}' | sudo tee /tmp/bad.json >/dev/nullsudo dockerd --validate --config-file /tmp/bad.jsonunable to configure the Docker daemon with file /tmp/bad.json: invalid JSON: invalid character '}' looking for beginning of object key string

    Expected resultA clear parse error, exit status 1, and a daemon that is still running normally.

    Success conditionYou have seen it fail safely. Had that file been installed and the daemon restarted, Docker would not have come back.

  3. Validate the real configuration, then install it

    Same command against the file you actually intend to use. configuration OK is what you want before the file goes anywhere near /etc/docker/.

    bash
    printf '%s' '{"log-driver":"json-file","log-opts":{"max-size":"10m","max-file":"3"},"live-restore":true}' | sudo tee /tmp/good.json >/dev/nullsudo dockerd --validate --config-file /tmp/good.jsonconfiguration OKsudo mkdir -p /etc/docker && sudo cp /tmp/good.json /etc/docker/daemon.json

    Expected resultconfiguration OK, then the file in place.

    Success conditionValidation passed before installation. Make this the habit - it costs one command and prevents the worst outcome.

  4. Reload rather than restart where you can

    systemctl reload docker sends SIGHUP, which re-reads the config without stopping containers. It handles a useful subset - logging defaults, live-restore, insecure registries, registry mirrors. Settings it cannot apply live, such as data-root, need a full restart.

    bash
    sudo systemctl reload dockerdocker info --format "logging={{.LoggingDriver}} live-restore={{.LiveRestoreEnabled}}"logging=json-file live-restore=true

    Expected resultlive-restore now true, with containers untouched.

    Success conditionThe setting applied without a restart. data-root and similar structural options are the exceptions - those require stopping the daemon.

  5. The trap: reload applies changes but does not un-apply them

    Now remove the file entirely and reload again. The setting does NOT revert - live-restore is still enabled with no configuration file present at all. SIGHUP merges what it finds; it does not reset options back to their defaults. Only a full restart clears them.

    bash
    sudo rm -f /etc/docker/daemon.json && sudo systemctl reload dockersudo test -f /etc/docker/daemon.json && echo present || echo "daemon.json absent"daemon.json absentdocker info --format "live-restore={{.LiveRestoreEnabled}}"live-restore=true

    Expected resultThe file gone, and the setting still in effect.

    Verify it worked

    bash Example session
    sudo systemctl restart dockerdocker info --format "live-restore={{.LiveRestoreEnabled}}"live-restore=false

    Success conditionYou have reproduced the trap. This is why a host can behave in ways its config file does not explain - somebody reloaded after removing a setting and assumed it had gone.

  6. The settings worth knowing

    A small set covers most real needs. data-root moves Docker's storage to a bigger disk and is the usual answer to a full root filesystem. Proxy settings let the daemon pull images through a corporate proxy - note these configure the DAEMON, not your containers. Log defaults apply rotation to every new container so nobody has to remember it.

    bash
    cat /etc/docker/daemon.json{  "data-root": "/mnt/docker",  "log-driver": "json-file",  "log-opts": { "max-size": "10m", "max-file": "3" },  "live-restore": true,  "default-address-pools": [{"base":"172.30.0.0/16","size":24}]}docker info --format "proxy: http={{.HTTPProxy}} https={{.HTTPSProxy}} no={{.NoProxy}}"proxy: http= https= no=

    Expected resultA representative configuration, and the daemon's current proxy settings.

    Success conditionYou can name what each does. default-address-pools is the fix when Docker's default bridge ranges collide with your corporate network.

  7. Daemon defaults are not stamped onto containers

    A subtlety worth knowing when debugging. With log rotation set as a daemon default, a new container's own LogConfig still shows empty options - the default is applied by the daemon at runtime rather than recorded on the container. So an empty map here does not mean rotation is off.

    bash
    docker run -d --name cg-cfg alpine:3.22 sleep 60docker inspect cg-cfg --format "{{.HostConfig.LogConfig.Type}} {{.HostConfig.LogConfig.Config}}"json-file map[]docker rm -f cg-cfg

    Expected resultAn empty options map on a container covered by a daemon-level default.

    Success conditionYou will not misread this as rotation being disabled. Check docker info for the daemon default, and the container only for per-container overrides.

Troubleshooting

Official sources