Docker daemon.json: Complete Configuration Reference 2026
The Docker daemon (dockerd) is the background service that manages Docker containers, images, networks, and volumes. While you can configure Docker using command-line flags, the preferred method is using the daemon.json configuration file. This 2026 reference covers every major option with annotated examples, validation steps, how to reload configuration without a full restart, and troubleshooting for common JSON syntax errors.
What is daemon.json?
The daemon.json file is a JSON configuration file that lets you customize Docker daemon behavior without modifying systemd service files or startup scripts. Changes persist across Docker updates and provide a cleaner, version-controllable configuration approach.
File Location
| Operating System | Location |
|---|---|
| Linux | /etc/docker/daemon.json |
| Windows | C:\ProgramData\docker\config\daemon.json |
| macOS (Docker Desktop) | ~/.docker/daemon.json or via Docker Desktop UI |
On a fresh Docker installation this file does not exist — you need to create it:
# Create the directory if it does not exist
sudo mkdir -p /etc/docker
# Create the file
sudo touch /etc/docker/daemon.json
All Major Configuration Options
log-driver and log-opts
Control how container logs are stored and rotated. The default driver is json-file, which writes logs to the local filesystem. Without size limits, logs can grow to fill the disk.
{
"log-driver": "json-file",
"log-opts": {
"max-size": "50m",
"max-file": "5",
"compress": "true"
}
}
Key log-opts parameters:
| Option | Description | Example |
|---|---|---|
max-size |
Maximum size before rotation | "10m", "100m", "1g" |
max-file |
Number of rotated files to keep | "3", "5" |
compress |
Compress rotated log files | "true" |
labels |
Attach container labels to log entries | "env,version" |
Available log drivers:
| Driver | Description |
|---|---|
json-file |
Default — logs to local JSON files |
syslog |
Writes to the system syslog |
journald |
Writes to systemd journal |
fluentd |
Sends logs to Fluentd |
awslogs |
Sends logs to Amazon CloudWatch |
gcplogs |
Sends logs to Google Cloud Logging |
splunk |
Sends logs to Splunk |
none |
Disables logging for all containers |
Centralized logging with Fluentd:
{
"log-driver": "fluentd",
"log-opts": {
"fluentd-address": "localhost:24224",
"tag": "docker.{{.Name}}"
}
}
storage-driver
The storage driver controls how image layers and container filesystems are stored on disk. overlay2 is the recommended choice for all modern Linux distributions.
{
"storage-driver": "overlay2"
}
With optional kernel check override (older kernels):
{
"storage-driver": "overlay2",
"storage-opts": [
"overlay2.override_kernel_check=true"
]
}
Available storage drivers:
| Driver | When to use |
|---|---|
overlay2 |
Recommended for all Linux (kernel 4.0+) |
fuse-overlayfs |
Rootless Docker mode |
btrfs |
Btrfs filesystem |
zfs |
ZFS filesystem |
devicemapper |
Legacy — not recommended |
To move the data root to a different disk:
{
"data-root": "/mnt/docker-data"
}
Warning: Changing the storage driver makes existing images and containers inaccessible. Always backup before changing.
insecure-registries
Allow Docker to communicate with private or development registries that do not have a valid TLS certificate:
{
"insecure-registries": [
"registry.local:5000",
"192.168.1.100:5000",
"internal-registry.company.local:5000"
]
}
Warning: Never use insecure registries in production. Configure proper TLS certificates for any registry handling sensitive images.
registry-mirrors
Registry mirrors speed up image pulls by caching Docker Hub images locally. Useful for CI/CD pipelines, large teams, or air-gapped environments.
{
"registry-mirrors": [
"https://mirror.gcr.io",
"https://docker-mirror.example.com"
]
}
For a self-hosted mirror (e.g., Harbor or a Docker distribution registry):
{
"registry-mirrors": [
"https://registry.internal.company.com:5000"
]
}
dns
Configure custom DNS servers applied to all containers. Useful when your network has internal DNS servers or Docker's default DNS resolution is unreliable:
{
"dns": ["8.8.8.8", "8.8.4.4"],
"dns-search": ["example.com", "corp.internal"],
"dns-opts": ["timeout:2", "attempts:3", "ndots:1"]
}
When to set custom DNS:
- Your host resolves internal hostnames that containers also need
- You want to ensure consistent DNS across all containers regardless of host configuration
- You are running in a corporate network with a split-horizon DNS
live-restore
Keep containers running when the Docker daemon is stopped, restarted, or upgraded. This is essential for production environments where container downtime must be minimized:
{
"live-restore": true
}
Benefits:
- Containers survive daemon restarts and upgrades
- Enables zero-downtime Docker upgrades on a host
- Reduces the blast radius of daemon crashes
Limitations:
- Not compatible with Docker Swarm mode
- Some daemon configuration changes still require restarting the affected containers
- Attaching to a container started before the daemon restart may not work
exec-opts (native.cgroupdriver=systemd)
This option is required when running Docker on a host managed by Kubernetes. Both the kubelet and the container runtime must use the same cgroup driver. The recommended value is systemd:
{
"exec-opts": ["native.cgroupdriver=systemd"]
}
Why this matters for Kubernetes:
If Docker uses the default cgroupfs driver while kubelet is configured for systemd, pods may fail to start and you will see errors like:
failed to create kubelet: misconfiguration: kubelet cgroup driver: "cgroupfs" is different from docker cgroup driver: "systemd"
Always set native.cgroupdriver=systemd on any node joining a Kubernetes cluster.
Full recommended configuration for a Kubernetes node:
{
"exec-opts": ["native.cgroupdriver=systemd"],
"log-driver": "json-file",
"log-opts": {
"max-size": "100m",
"max-file": "3"
},
"storage-driver": "overlay2",
"live-restore": true
}
default-ulimits
Set default resource limits for all containers. This prevents a runaway container from exhausting system file descriptors or processes:
{
"default-ulimits": {
"nofile": {
"Name": "nofile",
"Hard": 65536,
"Soft": 65536
},
"nproc": {
"Name": "nproc",
"Hard": 65536,
"Soft": 65536
},
"memlock": {
"Name": "memlock",
"Hard": -1,
"Soft": -1
}
}
}
memlock with -1 means unlimited — required for GPU workloads and some databases.
Additional Useful Options
Default address pools — prevent IP range conflicts with your network:
{
"default-address-pools": [
{"base": "172.20.0.0/16", "size": 24}
]
}
Metrics — expose a Prometheus scrape endpoint:
{
"metrics-addr": "127.0.0.1:9323",
"experimental": true
}
Debug mode — increase daemon log verbosity for troubleshooting:
{
"debug": true,
"log-level": "debug"
}
IPv6 support:
{
"ipv6": true,
"fixed-cidr-v6": "2001:db8:1::/64"
}
Security hardening:
{
"userns-remap": "default",
"no-new-privileges": true,
"userland-proxy": false
}
Complete Annotated daemon.json Example
Below is a production-ready daemon.json. JSON does not support comments natively — each key is explained in the table after the snippet. Use the JSON block directly without modifications.
{
"storage-driver": "overlay2",
"data-root": "/mnt/docker-data",
"log-driver": "json-file",
"log-opts": {
"max-size": "50m",
"max-file": "5",
"compress": "true"
},
"exec-opts": ["native.cgroupdriver=systemd"],
"live-restore": true,
"registry-mirrors": [
"https://mirror.gcr.io"
],
"insecure-registries": [],
"dns": ["8.8.8.8", "8.8.4.4"],
"default-ulimits": {
"nofile": {
"Name": "nofile",
"Hard": 65536,
"Soft": 65536
}
},
"default-address-pools": [
{"base": "172.20.0.0/16", "size": 24}
],
"metrics-addr": "127.0.0.1:9323",
"experimental": true,
"userland-proxy": false,
"no-new-privileges": true,
"log-level": "warn",
"max-concurrent-downloads": 10,
"max-concurrent-uploads": 5
}
Each key explained:
| Key | Purpose |
|---|---|
storage-driver |
Use overlay2 for best performance on modern kernels |
data-root |
Store Docker data on a dedicated volume |
log-driver / log-opts |
Log rotation — keep 5 files of up to 50 MB each, compressed |
exec-opts |
Required for Kubernetes nodes (systemd cgroup driver) |
live-restore |
Keep containers alive when the daemon restarts |
registry-mirrors |
Pull-through cache for Docker Hub images |
insecure-registries |
Private registries without TLS (keep empty in production) |
dns |
Default DNS for all containers |
default-ulimits |
Raise open file descriptor limits system-wide |
default-address-pools |
Prevent IP range conflicts with corporate network |
metrics-addr |
Prometheus metrics endpoint (localhost only) |
userland-proxy |
Disable for better network performance |
no-new-privileges |
Security: prevent privilege escalation inside containers |
log-level |
Reduce daemon log noise in production |
max-concurrent-downloads/uploads |
Increase parallel image throughput |
Validate daemon.json Before Restarting
Method 1: dockerd --validate (Docker 23.0+)
Docker 23.0 introduced a built-in validation flag:
sudo dockerd --validate --config-file /etc/docker/daemon.json
- Exit code 0: configuration is valid.
- Non-zero exit with error message: fix the reported issue before restarting.
Method 2: JSON syntax check with python3
cat /etc/docker/daemon.json | python3 -m json.tool
A clean JSON file produces formatted output with no errors. Any syntax error is printed with the line number.
Method 3: jq
cat /etc/docker/daemon.json | jq .
Both python3 -m json.tool and jq catch JSON syntax errors but cannot validate Docker-specific option names and types. Use dockerd --validate for full semantic validation when available.
Reload Config Without a Full Restart
Some daemon options can be applied without restarting Docker by sending SIGHUP to the daemon process:
sudo kill -SIGHUP $(pidof dockerd)
Options that support SIGHUP reload (no restart needed):
debuglog-levellabelslive-restorecluster-storecluster-advertisemax-concurrent-downloadsmax-concurrent-uploads
Options that require a full daemon restart:
storage-driverexec-optsdata-rootlog-driver/log-optsinsecure-registriesregistry-mirrorsdns
Full restart procedure:
# 1. Validate the new configuration
sudo dockerd --validate --config-file /etc/docker/daemon.json
# 2. Reload systemd (if you also edited the service file)
sudo systemctl daemon-reload
# 3. Restart Docker
sudo systemctl restart docker
# 4. Confirm Docker is healthy
sudo systemctl status docker
docker info | head -30
Troubleshooting: Common JSON Syntax Errors
Error: "unable to configure the Docker daemon with file"
Docker emits this error when daemon.json is invalid. Check the daemon logs:
sudo journalctl -u docker.service -n 30
Then validate the file:
cat /etc/docker/daemon.json | python3 -m json.tool
Most common JSON mistakes:
| Mistake | Bad example | Fixed example |
|---|---|---|
| Trailing comma after last item | {"debug": true,} |
{"debug": true} |
| Single quotes instead of double | {'debug': true} |
{"debug": true} |
| Missing comma between items | {"debug": true "log-level": "info"} |
{"debug": true, "log-level": "info"} |
| Unquoted string value | {"log-level": info} |
{"log-level": "info"} |
| Boolean as string | {"live-restore": "true"} |
{"live-restore": true} |
| Integer as string | {"max-concurrent-downloads": "10"} |
{"max-concurrent-downloads": 10} |
Error: "Conflicting options: option already set in file"
You cannot set the same option in both daemon.json and Docker's systemd service flags. Check the service file:
sudo systemctl cat docker.service
Look for the ExecStart line. Any flag like --log-driver, --storage-driver, or --dns must be removed if the same key exists in daemon.json.
Error: Docker starts but containers cannot reach the network
Check for IP range conflicts:
docker network inspect bridge
ip route
If Docker's default 172.17.0.0/16 overlaps with your corporate network, set a different range in daemon.json:
{
"bip": "192.168.200.1/24",
"default-address-pools": [
{"base": "192.168.201.0/24", "size": 28}
]
}
Reset to Default Configuration
If Docker will not start after a bad change:
sudo mv /etc/docker/daemon.json /etc/docker/daemon.json.backup
sudo systemctl start docker
Fix the backup file, then restore it and restart.
How to Apply Changes
systemd-based systems (Ubuntu, Debian, CentOS 7+, Fedora, RHEL)
sudo systemctl daemon-reload
sudo systemctl restart docker
sudo systemctl status docker
Older init systems
sudo service docker restart
Verify Applied Configuration
# Human-readable summary
docker info
# Check specific settings
docker info --format '{{.LoggingDriver}}'
docker info --format '{{.Driver}}'
docker info --format '{{.CgroupDriver}}'
Environment-Specific Configuration Examples
Kubernetes Node
{
"exec-opts": ["native.cgroupdriver=systemd"],
"log-driver": "json-file",
"log-opts": {"max-size": "100m", "max-file": "3"},
"storage-driver": "overlay2",
"live-restore": true
}
Development Environment
{
"storage-driver": "overlay2",
"log-driver": "json-file",
"log-opts": {"max-size": "10m", "max-file": "3"},
"dns": ["8.8.8.8", "8.8.4.4"],
"insecure-registries": ["localhost:5000"],
"debug": true,
"experimental": true,
"features": {"buildkit": true}
}
CI/CD Build Server
{
"storage-driver": "overlay2",
"log-driver": "json-file",
"log-opts": {"max-size": "20m", "max-file": "3"},
"registry-mirrors": ["https://mirror.gcr.io"],
"max-concurrent-downloads": 10,
"max-concurrent-uploads": 5,
"features": {"buildkit": true}
}
Air-Gapped / Restricted Network
{
"storage-driver": "overlay2",
"registry-mirrors": ["https://internal-registry.company.com:5000"],
"insecure-registries": ["internal-registry.company.com:5000"],
"dns": ["10.0.0.53"],
"dns-search": ["company.internal"],
"log-driver": "syslog",
"log-opts": {
"syslog-address": "udp://10.0.0.100:514",
"tag": "docker/{{.Name}}"
},
"default-address-pools": [{"base": "10.200.0.0/16", "size": 24}]
}
NVIDIA GPU Host
{
"storage-driver": "overlay2",
"default-runtime": "nvidia",
"runtimes": {
"nvidia": {
"path": "nvidia-container-runtime",
"runtimeArgs": []
}
},
"log-driver": "json-file",
"log-opts": {"max-size": "50m", "max-file": "3"},
"default-ulimits": {
"memlock": {"Name": "memlock", "Hard": -1, "Soft": -1}
}
}
Related Docker Guides
- How to Use Nginx as a Reverse Proxy with Docker - Proxy containers behind Nginx
- GitLab CI: Push Docker Images to Registry - CI/CD pipeline integration
- How to Stop or Remove All Docker Containers - Container lifecycle management
Summary
The daemon.json file is the recommended way to configure Docker. Key takeaways for 2026:
- Always validate before restarting:
sudo dockerd --validate --config-file /etc/docker/daemon.json - Reload without restart for supported options:
sudo kill -SIGHUP $(pidof dockerd) - Kubernetes nodes must set
"exec-opts": ["native.cgroupdriver=systemd"] - Enable log rotation in every environment — the default
json-filedriver has no size limit unless you configuremax-sizeandmax-file - Enable
live-restoreon production hosts to keep containers running during daemon upgrades - No conflicts — never duplicate an option between
daemon.jsonand systemd service flags
For the complete list of available options, refer to the official Docker daemon documentation.