Initializing Enclave...

How to Fix Docker daemon.json Parse Error: Invalid Character Crashing the Docker Daemon

Threat/Impact Level: CRITICAL | Exploitability/Downtime Risk: HIGH | Time to Fix: 5 mins


TL;DR

  • What broke: dockerd failed to start because /etc/docker/daemon.json contains an invalid character — trailing comma, single quote, unquoted key, or a UTF-8 BOM — making the entire JSON unparseable.
  • How to fix it: Run dockerd --validate or python3 -m json.tool /etc/docker/daemon.json to pinpoint the offending line, then correct the syntax.
  • Fast path: Use our Client-Side Sandbox above to paste your daemon.json and auto-refactor it instantly without sending your config anywhere.

The Incident (What Does the Error Mean?)

The raw error from journalctl -u docker.service or systemctl status docker looks like this:

failed to load daemon configuration file: /etc/docker/daemon.json:
  invalid character '}' looking for beginning of object key string

or

failed to load daemon configuration file: /etc/docker/daemon.json:
  invalid character '\'' looking for beginning of value

Immediate consequence: dockerd refuses to start. Every container on the host — including sidecars, monitoring agents, and any service depending on the Docker socket — is dead. On a Swarm manager node or a CI runner host, this is a full outage. There is no graceful degradation; the daemon either parses the config cleanly or it does not start at all.


The Attack Vector / Blast Radius

This is not a security exploit, but the blast radius on a production host is severe:

  • Single host: All running containers are orphaned. docker ps returns Cannot connect to the Docker daemon.
  • Swarm node: A manager going down mid-deployment can leave the cluster in a split-brain scheduling state.
  • CI/CD runners: Every pipeline job on the affected runner fails immediately. Depending on your runner pool size, this can stall your entire deployment pipeline.
  • Kubernetes (Docker shim or containerd fallback misconfigured): If you edited daemon.json to configure insecure-registries or log-driver for a node, that node's kubelet loses its container runtime and the node goes NotReady, triggering pod evictions across the cluster.

The most common causes in order of frequency:

  1. Trailing comma after the last key-value pair (the #1 offender — copy-pasted from JavaScript-tolerant editors)
  2. Single quotes instead of double quotes
  3. Unquoted string values
  4. UTF-8 BOM injected by Windows editors (Notepad, some VS Code configs) when the file is transferred to Linux
  5. Comments (// or #) — JSON does not support comments

How to Fix It

Step 0: Validate Before Editing

# Pinpoint the exact line and character offset
python3 -m json.tool /etc/docker/daemon.json

# Or use jq
jq . /etc/docker/daemon.json

# Docker's own validator (Docker 23.0+)
dockerd --validate --config-file /etc/docker/daemon.json

Basic Fix: Trailing Comma (Most Common)

{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
- },
- "insecure-registries": ["registry.internal:5000"],
+ },
+ "insecure-registries": ["registry.internal:5000"]
}

Basic Fix: Single Quotes

{
- 'log-driver': 'json-file',
- 'storage-driver': 'overlay2'
+ "log-driver": "json-file",
+ "storage-driver": "overlay2"
}

Basic Fix: Stripping a UTF-8 BOM

If file /etc/docker/daemon.json returns UTF-8 Unicode (with BOM), the invisible \xEF\xBB\xBF prefix at byte 0 is your invalid character.

# Strip BOM in-place
sed -i '1s/^\xef\xbb\xbf//' /etc/docker/daemon.json

# Verify
file /etc/docker/daemon.json
# Expected: /etc/docker/daemon.json: ASCII text

Enterprise Best Practice: Managed daemon.json via Configuration Management

Stop editing daemon.json by hand on live hosts. Manage it through Ansible, Chef, or Puppet with a schema-validated template.

Ansible task with pre-flight validation:

- name: Copy daemon.json directly
-   copy:
-     src: files/daemon.json
-     dest: /etc/docker/daemon.json

+ name: Validate daemon.json locally before deploying
+   delegate_to: localhost
+   command: python3 -m json.tool {{ role_path }}/files/daemon.json
+   changed_when: false
+
+ name: Deploy validated daemon.json
+   copy:
+     src: files/daemon.json
+     dest: /etc/docker/daemon.json
+     owner: root
+     group: root
+     mode: '0644'
+   notify: restart docker

Canonical, production-safe daemon.json structure:

{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "20m",
    "max-file": "5"
  },
  "storage-driver": "overlay2",
  "insecure-registries": [],
  "live-restore": true,
  "userland-proxy": false,
  "metrics-addr": "127.0.0.1:9323"
}

Note: live-restore: true is the single most important production setting — it keeps containers running during a daemon restart, giving you a window to fix config errors without a full outage.


💡 Tired of pasting proprietary configs into ChatGPT? Generic AI tools log your company's ARNs, DB strings, and private keys. StackEngine is a zero-backend, pure Client-Side WASM utility. Drop your failing config into the sandbox above. We redact your secrets locally in the browser and auto-generate the refactored code using your own API key.


Prevention in CI/CD

1. Pre-commit Hook (fastest feedback loop)

# .git/hooks/pre-commit
if git diff --cached --name-only | grep -q 'daemon.json'; then
  python3 -m json.tool /etc/docker/daemon.json > /dev/null || {
    echo "ERROR: daemon.json is invalid JSON. Commit blocked."
    exit 1
  }
fi

2. Checkov IaC Scan

If your daemon.json is rendered from a Terraform templatefile() or a Helm values file:

checkov -f daemon.json --framework json

3. GitHub Actions Validation Step

- name: Validate Docker daemon config
  run: |
    jq empty ansible/roles/docker/files/daemon.json
    echo "daemon.json is valid JSON"

4. OPA Policy (Conftest) for Structural Rules

Enforce that live-restore is always true and insecure-registries is never populated in production:

package docker.daemon

deny[msg] {
  input["live-restore"] != true
  msg := "live-restore must be true in production daemon.json"
}

deny[msg] {
  count(input["insecure-registries"]) > 0
  msg := "insecure-registries must be empty in production"
}
conftest test daemon.json --policy docker-policy/

This catches both syntax errors (conftest will refuse to parse invalid JSON) and semantic misconfigurations before any file touches a production host.

Related Diagnostics

"Part of the Syntax Utility Matrix."

View all 153 Syntax Tools →