Cannot connect to the Docker daemon — 5 Reasons and How to Diagnose Each
Symptom: every docker command gets rejected
Cannot connect to the Docker daemon at unix:///var/run/docker.sock.
Is the docker daemon running?
One-line diagnosis: the docker CLI client is alive, but the dockerd daemon it talks to is not. The client reaches the daemon through a Unix socket (or TCP/SSH); if nothing answers there, this is the error. Note the difference from permission denied — that one means "the socket exists but you may not touch it"; this one means "the socket doesn't exist or nobody is behind it".
Five causes, ranked by frequency
1. The daemon isn't running (most common on Linux servers)
systemctl status docker
# ● docker.service - Docker Application Container Engine
# Active: inactive (dead) ← not running
sudo systemctl start docker
sudo systemctl enable docker # and start it on boot while you're at it
Forgotten enable after a reboot, a service killed by an upgrade, or the OOM killer taking down dockerd — they all land here. Check the status before anything else.
2. Docker Desktop isn't open (macOS / Windows)
With Docker Desktop, the daemon runs inside a VM, but the CLI is separate — the App can be closed while the CLI still exists, producing the identical error. Start Docker Desktop and wait for the whale icon to settle.
3. WSL2: the distro integration got switched off
Using docker inside WSL2 on Windows requires Docker Desktop's integration for that distro (Settings → Resources → WSL Integration). An upgrade that resets the toggle is a frequent visitor:
# Restart WSL, then retry
wsl --shutdown
4. A broken daemon.json prevents the daemon from starting
When /etc/docker/daemon.json has a syntax error or an invalid field, systemctl start docker appears to succeed but actually fails. Get the real reason:
journalctl -u docker --since "10 minutes ago" | tail -20
# Common output: unable to configure the Docker daemon with file ...: invalid character
Fix the JSON (note: no trailing comma after the last key) and restart. This is the classic trap when configuring registry mirrors or log rotation.
5. Wrong docker context / missing rootless env var
docker context ls
# NAME DESCRIPTION DOCKER ENDPOINT
# default * Docker Engine at unix:///var/run/... ← asterisk = current context
If the asterisk sits on a remote or stale context, even a perfectly healthy local daemon won't be reached: docker context use default.
Under rootless mode the socket lives in your user directory (/run/user/<uid>/docker.sock); a fresh SSH session without export DOCKER_HOST=unix:///run/user/$(id -u)/docker.sock will probe a system socket that doesn't exist.
The diagnostic route (two minutes, top to bottom)
# 1. Service status
systemctl status docker
# 2. Not active? Read the logs for the cause of death
journalctl -u docker -n 30
# 3. Service fine but still failing? Check socket and context
ls -l /var/run/docker.sock
docker context ls
# 4. Everything normal? Ask the daemon directly
sudo curl -s --unix-socket /var/run/docker.sock http://localhost/version | head -c 200
If step 4 returns JSON, the daemon is alive — the problem is your user's permission. See: Docker socket permission denied.
Telling the neighboring errors apart
| Error | Meaning | Guide |
|---|---|---|
| Cannot connect to the Docker daemon | daemon not running / unreachable | this post |
| permission denied ... docker.sock | socket exists, your user lacks access | permissions post |
| no space left on device | daemon alive, disk full | disk cleanup post |
| port is already allocated | daemon alive, port taken | port conflict post |
Rootless and user-mode daemons: the new startup paths
Docker Desktop (Mac/Windows): the daemon runs inside a utility VM — "cannot connect" means the VM/app is not started (launch the app; the CLI alone does not boot it), and CLI contexts can point at the wrong endpoint after upgrades (docker context ls; the desktop-linux context is the default — a stale context from a Docker Machine era lingers). Rootless Docker on Linux: the daemon runs per-user via systemd user units (loginctl enable-linger for the user so the daemon survives logout — the "it worked yesterday, daemon dead today" on shared boxes is linger missing). Podman compatibility: the docker CLI hitting podman's socket (an alias setup) has its own daemon-less model — docker context show names the active endpoint; knowing which daemon model your machine runs is step zero of every startup diagnosis.
The socket itself: permissions and existence
/var/run/docker.sock is the CLI-to-daemon contract: missing (fresh boot before the service, or a snap install with its own socket path — snap docker uses /run/snap.docker.sock, scripts referencing the classic path fail), permission-denied (user not in docker group — membership applies on next login, not immediately: newgrp docker or re-login; the chmod-666 workaround is root-equivalent-for-everyone and should never survive the day), or a stale socket file after a crash (the daemon fails to start because the old socket exists — systemd usually cleans, manual cases need the file removed before restart). ls -l on the socket and ss -lx | grep docker answer all three variants in ten seconds.
systemd units and the restart discipline
Modern Linux: dockerd runs as a systemd service (docker.service; containerd as its dependency socket/service — containerd dead takes docker with it: systemctl status containerd before blaming docker). The restart discipline: systemctl restart docker is safe for the daemon but kills running containers under the default live-restore=off (enable live-restore in daemon.json for containers to survive daemon restarts — the difference between a blip and an outage), and restart-loops (the service flapping) usually mean a config error in daemon.json — journalctl -u docker -n 50 shows the parse error in the first lines. The habit that prevents the whole class: a post-restart container check (docker ps against the expected list) wired into whatever restarts the daemon.
Checklist
systemctl status docker— is the service active?- macOS/Windows — is Docker Desktop running? Is WSL integration on?
journalctl -u docker -n 30— any daemon.json syntax errors?docker context ls— is the asterisk on default?- rootless — is
DOCKER_HOSTset for this session?
After the daemon is back: confirm the fleet
A successful docker info only proves the socket answers. Follow with docker ps -a against the containers you expect, and check docker compose ls if stacks own the workloads. Live-restore off means a daemon restart may have exited containers that look “missing” rather than “daemon down.” Document which services auto-restart (restart: unless-stopped) so the next outage does not turn into a manual docker start scavenger hunt.
Triage notes
The five causes are ranked by probability: on Linux, "service not started" leads because a forgotten enable after reboot, a failed upgrade, and an OOM kill all land in that bucket; Docker Desktop comes second because its CLI exists independently while the daemon runs in a VM, so the error reads identically with the app closed. Step 4 of the diagnostic path — querying the daemon with curl — is a deliberate fork: getting JSON back means the daemon is alive and the problem is user permissions, the territory of the permission-denied article. The dividing line between the two pieces is one sentence: is the socket missing entirely, or present but off-limits to you?
Provided by ToolVault. Related tools: Dockerfile Generator, Run to Compose, Linux Cheatsheet. Related reading: Docker socket permissions, Port already in use (EADDRINUSE). See the homepage for more developer tools.
Related Tools
Related Articles
Linux Crontab in Practice: Commands, Logs, and Debugging
Crontab beyond syntax: three must-know commands, /etc/cron.d vs user crontabs, five-step debugging, PATH/timezone/% escaping, MAILTO, and what to do when you need second-level scheduling.
docker run to docker-compose: The Complete Migration Guide (with Flag Map)
Migrate docker run commands to docker-compose.yml: five advantages of compose over raw run, a flag-by-flag mapping table (ports, volumes, env, restart, networks), the classic pitfalls (bare -e passthrough, anonymous volumes, --rm semantics), and an online converter that runs locally in your browser.
Permission denied (publickey): 6 Reasons Git Push Fails Over SSH (and the Fix for Each)
git clone or push rejected with Permission denied (publickey) fatal: Could not read from remote repository? Covers missing keygen, key not loaded in the agent, public key not added to GitHub/GitLab, multi-account key routing with ~/.ssh/config, deploy key limits, and wrong remote URLs — with ssh -v diagnostics.