The daemon error almost never means Docker is broken. Nine times out of ten it's a stopped service, a stale context, or a wrong env var. Here's the checklist.
You run docker ps, expecting a list of containers, and instead get this:
Cannot connect to the Docker daemon at unix:///var/run/docker.sock.
Is the docker daemon running?
I've hit this on fresh laptops, in CI runners, over SSH into boxes I'd forgotten I configured, and once on my own machine an hour after I'd installed Docker and walked away. The message reads like something is badly broken. It almost never is. The CLI is a thin client that talks to a daemon over a socket, and this error means the client couldn't reach that socket. That's it. Every fix below is about answering one question: which daemon should I be talking to, and is it up?
Work through these in order. The whole thing takes a couple of minutes and you'll usually be done by step two.
Before touching services, check what the CLI thinks it should connect to. Two commands:
echo $DOCKER_HOST
docker context ls
If DOCKER_HOST is set to something you don't recognize (an old tcp:// address, a remote box that's long gone), that's your answer. I've seen this survive in a .bashrc from a tutorial someone followed two years ago. The CLI dutifully tries to reach a machine that no longer exists.
unset DOCKER_HOST
Then retry docker ps. If it works, go delete that export from your shell profile so it doesn't come back tomorrow.
docker context ls shows the same thing from the other side. The active context has a * next to it:
NAME DESCRIPTION DOCKER ENDPOINT
default * Current DOCKER_HOST based unix:///var/run/docker.sock
desktop-linux Docker Desktop unix:///home/me/.docker/desktop/docker.sock
If the star is on a context whose endpoint points somewhere unreachable, switch back:
docker context use default
This one bites people who install Docker Desktop and later install the native engine, or vice versa. The context gets left pointing at a socket that isn't being served anymore. docker context use is the whole fix.
On a Linux host with the native engine, the daemon runs as a systemd service. Ask systemd directly:
systemctl status docker
If you see Active: inactive (dead) or failed, start it:
sudo systemctl start docker
sudo systemctl enable docker # so it comes back after a reboot
I add the enable line because "it worked yesterday" after a reboot is the single most common way this error comes back. Fresh installs of the engine on some distros don't enable the service by default.
If the service refuses to start, it will loop in status. Don't guess at why. Read the logs:
journalctl -u docker --no-pager -n 40
The real cause is almost always in the last twenty lines. A corrupt daemon.json, a bad storage driver, a port already bound. I once spent ten minutes on a "mystery" that turned out to be a trailing comma in /etc/docker/daemon.json. The journal told me on line three; I just hadn't read it.
On Docker Desktop (Mac, Windows, or Linux), there's no systemd service to poke. The daemon lives inside Desktop's VM, and if Desktop isn't running, the socket isn't served. Open the app, wait for the whale icon to stop animating, then retry. Obvious, but it's caught me after a reboot more than once.
If ls -l /var/run/docker.sock returns "No such file or directory," no daemon has created it. That's the running-daemon problem above, not a separate bug. Start the service and the socket appears.
There's a lookalike. If the daemon is up but your user isn't in the docker group, you get a permission-denied on the socket, which the CLI sometimes phrases close enough to the connection error to confuse you. Quick tell: sudo docker ps works but plain docker ps doesn't. If that's your situation, it's a group membership problem, and I've written up the Docker daemon permission fixes separately so I won't repeat them here.
Rootless Docker doesn't use /var/run/docker.sock. It runs the daemon as your user, and the socket lives under your runtime dir:
export DOCKER_HOST=unix:///run/user/$(id -u)/docker.sock
systemctl --user status docker
Note the --user. Rootless is a user service, not a system one, so sudo systemctl will tell you nothing useful. If you set up rootless and then wonder why systemctl status docker says the unit doesn't exist, this is why.
WSL2 has its own trap. If you're running the Docker CLI inside a WSL2 distro and talking to Docker Desktop on Windows, that link is Desktop's WSL integration, and it's a per-distro toggle. Open Desktop, go to Settings, Resources, WSL Integration, and confirm your distro is switched on. When that toggle is off, the CLI inside WSL has no socket to reach and you get the exact daemon error. Flip it on, restart the shell.
When I hit this cold, I run these four in a row before changing anything:
echo $DOCKER_HOST # is the client aimed somewhere weird?
docker context ls # is the active context sane?
systemctl status docker # is the daemon actually up? (or --user for rootless)
journalctl -u docker -n 40 # if it won't start, why not?
That sequence separates the "client is pointed wrong" cases (the first two) from the "daemon is down" cases (the last two), and those two families cover essentially every instance of this error I've seen.
Don't reinstall Docker. I've watched people nuke a working install over this error when the actual problem was a stale DOCKER_HOST in their shell. Check where the client points, confirm the daemon is up, read the journal if it won't start. The error is loud and the fix is usually one line. Treat it as a routing question, not a broken-engine question, and you'll close it in under two minutes.
Get the latest tutorials, guides, and insights on AI, DevOps, Cloud, and Infrastructure delivered directly to your inbox.
Our best engineer quit citing on-call. We rebuilt the whole thing: saner rotations, runbooks that actually help at 3am, and escalation that doesn't punish asking for help.
We ripped every client secret out of our CI pipelines by pointing Azure federated credentials at GitHub's OIDC issuer. Here's the exact setup and the claims that trip people up.
Explore more articles in this category
The IaC landscape fractured after the Terraform license change. This is the map to what each tool is actually best at, and how to choose without regret.
Terragrunt keeps large Terraform setups DRY and orchestrated, but small teams often pay its learning curve for little return.
A practical comparison of Kubernetes-native continuous reconciliation against the classic CLI-driven, state-file IaC model for platform teams.
Evergreen posts worth revisiting.