Skip to main content

Explain and Harden a Docker daemon.json Configuration

·793 words·4 mins·
Photo by Thomas Wolter on Unsplash
The daemon.json file is one of the highest-leverage Docker files in production. A few keys can improve container reliability, host safety, and observability. In this post, we review a real-world configuration and explain what each setting does, why it matters, and what to validate before rollout.

Disclaimer
#

This configuration is a strong starting point for many production environments, but it is not a one-size-fits-all solution. Always test changes in staging and monitor closely after deployment. The best settings depend on your specific workloads, security requirements, and operational practices.

Configuration Under Review
#

{
  "default-shm-size": "64M",
  "default-ulimits": {
    "nofile": {
      "Hard": 65535,
      "Name": "nofile",
      "Soft": 65535
    }
  },
  "dns": [
    "{{ dns_primary_ip }}",
    "{{ dns_secondary_ip }}"
  ],
  "live-restore": true,
  "log-driver": "local",
  "log-opts": {
    "compress": "true",
    "max-file": "5",
    "max-size": "10m"
  },
  "no-new-privileges": true,
  "registry-mirrors": [
    "{{ registry_mirror_url }}"
  ],
  "storage-driver": "overlay2"
}
The {{ ... }} values indicate template placeholders (for example with Ansible, Helm-like templating, or a custom CI render step). Deploying this file without rendering those values first will lead to misconfiguration.

Key-by-Key Analysis
#

default-shm-size: "64M"
#

Docker mounts /dev/shm as tmpfs inside containers. The default is often too small for browsers, headless tests, PostgreSQL shared memory usage, and data-heavy IPC workloads.

What this gives you:

  • Predictable shared memory baseline for all containers.
  • Fewer random crashes due to No space left on device in /dev/shm.

What to watch:

  • 64M may still be too low for Chrome/Playwright, ML, or large in-memory jobs.
  • Prefer setting higher values per workload when needed (--shm-size=256m or Compose equivalent).

default-ulimits.nofile = 65535
#

This raises file descriptor limits for containers by default.

Why it matters:

  • High-concurrency services (reverse proxies, brokers, DB clients) consume many file descriptors.
  • Prevents hard-to-debug failures such as too many open files under load.

Operational advice:

  • Match host-level limits (/etc/security/limits.conf, systemd service limits) to avoid a false sense of safety.
  • Validate under stress with realistic connection counts.

dns: ["{{ dns_primary_ip }}", "{{ dns_secondary_ip }}"]
#

This forces explicit DNS resolvers for containers instead of inheriting host defaults.

Benefits:

  • Stable, deterministic name resolution across environments.
  • Easier troubleshooting when host DNS stacks differ.

Risks:

  • If resolver IPs are unreachable, all container DNS resolution degrades.
  • Hard-coding resolvers can bypass local split-DNS behavior if not planned.

Best practice:

  • Use two independent resolvers.
  • Add health checks and monitoring for resolver availability.

live-restore: true
#

Keeps containers running during daemon upgrades/restarts (where supported).

Why it is valuable:

  • Reduces app impact during Docker daemon maintenance.
  • Good fit for hosts where short daemon restarts are common.

Limitations:

  • Not a replacement for orchestrator-level high availability.
  • Network/control-plane operations may still be temporarily impacted.

log-driver: "local" with rotation options
#

"log-driver": "local",
"log-opts": {
  "compress": "true",
  "max-file": "5",
  "max-size": "10m"
}

This is a strong production default.

Why:

  • local is more space-efficient than json-file in many scenarios.
  • Rotation (5 x 10m) caps per-container disk growth.
  • Compression reduces log storage pressure.

Trade-offs:

  • Very high verbosity workloads may still require centralized logging.
  • Keep an eye on total host log usage when container count grows.

no-new-privileges: true
#

Prevents privilege escalation via setuid/setgid binaries and similar mechanisms in containers.

Security impact:

  • Excellent baseline hardening control.
  • Helps enforce least privilege behavior in container processes.

Compatibility note:

  • Rare images relying on privilege elevation may break and need redesign.

registry-mirrors: ["{{ registry_mirror_url }}"]
#

Configures a pull-through mirror or internal registry proxy.

Advantages:

  • Faster image pulls.
  • Better resilience against external registry rate limits.
  • Lower internet egress and improved supply-chain control.

What to validate:

  • Mirror freshness and cache policy.
  • TLS trust chain and authentication to the mirror.

storage-driver: "overlay2"
#

overlay2 is the recommended Linux storage driver for most modern distributions and kernels.

Strengths:

  • Mature and performant for general container workloads.
  • Broad compatibility with mainstream Docker installations.

Caution:

  • Confirm backing filesystem compatibility and kernel support.
  • Never change storage driver on a running node without migration planning.

Deployment Checklist
#

  1. Render template placeholders before deployment.
  2. Validate JSON syntax.
  3. Apply configuration and restart Docker.
  4. Verify daemon settings took effect.
  5. Run a smoke test with representative workloads.
# 1) Render template (example path)
cat /etc/docker/daemon.json

# 2) Validate JSON syntax
jq . /etc/docker/daemon.json

# 3) Restart Docker daemon
sudo systemctl restart docker

# 4) Verify effective configuration
docker info | grep -E "Logging Driver|Cgroup|Storage Driver|Live Restore Enabled"
docker info --format '{{json .}}' | jq '.RegistryConfig.Mirrors'

# 5) Functional checks
docker run --rm busybox nslookup example.com
docker run --rm busybox sh -c 'ulimit -n'

Recommended Next Improvements#

  • Add centralized log shipping (Loki, Elasticsearch, OpenSearch, or a managed platform).
  • Enforce image provenance and signature verification in CI/CD.
  • Run containers as non-root where possible (USER in Dockerfile).
  • Define daemon config as code and roll it out with canary hosts first.
  • Implement monitoring and alerting for Docker daemon health and resource usage.