Home DevOps Cannot Connect to Docker Daemon: Fix It Fast
Beginner 6 min · September 23, 2026

Cannot Connect to Docker Daemon: Fix It Fast

Start dockerd with sudo systemctl start docker, clear a stale DOCKER_HOST, and join the docker group.

N
Naren Founder & Principal Engineer

20+ years shipping production infrastructure and CI/CD at scale. Drawn from code that ran under real load.

Follow
Production
production tested
September 23, 2026
last updated
1,905
articles · all by Naren
Before you start⏱ 10 min
  • A machine with Docker installed (Linux server or Docker Desktop on Mac/Windows)
  • A terminal with sudo or admin access for service and group changes
  • Basic comfort running shell commands and reading command output
 ● Production Incident 🔎 Debug Guide
Quick Answer
  • The Docker CLI is only a client — this error means it can't reach the daemon (dockerd) through the socket or TCP address it was given
  • Start the daemon first: sudo systemctl start docker on Linux, or launch Docker Desktop on Mac and Windows, then re-run docker info
  • A stale DOCKER_HOST is the next suspect: run echo $DOCKER_HOST and unset DOCKER_HOST if it points at a machine that's gone
  • Permission denied on the socket means you're outside the docker group: sudo usermod -aG docker $USER, then log out and back in
✦ Definition~90s read
What is Cannot Connect to Docker Daemon Fix?

Docker is a client-server system, and that split is the whole story behind this error. When you run docker ps, the docker binary in your PATH doesn't list containers itself — it builds an HTTP request and sends it to the Docker daemon (dockerd), the background process that actually manages images, containers, networks, and volumes.

Think of a restaurant where you're the waiter and the kitchen is the chef.

The CLI figures out where to send that request from three sources, checked in order: the DOCKER_HOST environment variable, the active Docker context in ~/.docker/contexts, and finally the default unix socket at /var/run/docker.sock on Linux (or ~/.docker/run/docker.sock under Docker Desktop).

"Cannot connect" means the TCP dial or socket dial failed. Either nothing is listening (dockerd isn't running), the address is wrong (DOCKER_HOST names a dead host, or the context points at a retired remote engine), or the connection is refused at the door (your user can't write to docker.sock, or TLS credentials for a remote daemon don't match).

Note the error says nothing about images, registries, or Dockerfiles — the request never got far enough for any of that to matter.

What this error is NOT: it isn't a broken image, a bad Dockerfile, a registry outage, or a full disk (a full disk produces its own "no space left" error after connecting). It also isn't fixed by docker login, image pruning, or reinstalling the CLI — reinstalling the client when the server is down changes nothing.

Treat it as a connectivity problem between two processes, diagnose it like one, and you'll fix it in minutes instead of hours.

Plain-English First

Think of a restaurant where you're the waiter and the kitchen is the chef. You shout orders through a serving hatch and the chef cooks. The Docker CLI is the waiter, the daemon is the chef, and the socket is the hatch. "Cannot connect" means you're shouting into a closed hatch — the kitchen is shut, you walked to the wrong restaurant (a stale DOCKER_HOST), or security won't let you through (socket permissions). Don't yell louder: open the kitchen, go to the right building, or show your badge.

It's 9 AM, you type docker ps, and the terminal answers with "Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?" Nothing you changed yesterday explains it. Your images are still there, your compose file is untouched, and Docker worked fine on Friday. This error is the single most common Docker failure, and it's also one of the most misdiagnosed — engineers rebuild images, reinstall Docker, and reboot laptops before checking the three things that actually cause it.

The confusion comes from the error's many disguises. On Linux it mentions a unix socket. On Mac it may complain about a missing socket in ~/.docker/run. In CI it shows up as a TCP dial timeout to some builder host nobody remembers configuring. Underneath, every variant means the same thing: the docker CLI knocked on a door and nobody answered.

This guide walks through the four doors in order: a daemon that isn't running, a DOCKER_HOST pointing at the wrong place, socket permissions that lock you out, and a Docker context stuck on a remote engine. You'll get the exact commands to confirm each one, the fix that sticks, and the guards that stop it from ruining another morning.

The Client Is Fine — the Daemon Isn't Answering

Start with the mental model, because it makes every later step obvious. The docker binary is a thin HTTP client. dockerd is the server that owns all state: images, containers, volumes, networks. Between them sits either a unix socket (fast, local, file-permission gated) or a TCP/SSH endpoint (remote builders, CI fleets, Docker Desktop's VM forwarding). "Cannot connect" is purely a dial failure — the request never reached any Docker logic, so no image, registry, or Dockerfile theory can explain it.

Your first move is always docker info. Unlike docker ps, which hides the target, the info error names the address it tried: unix:///var/run/docker.sock, tcp://10.0.4.51:2376, or ssh://builder. That address is the entire diagnosis compressed into one line — it tells you whether you're fighting a local daemon, a stale remote, or a context you forgot. Pair it with docker version, which splits client vs server output: a client version with a failed server section proves the CLI is healthy and the daemon side is the problem.

The resolution order never changes: is anything listening, is the address correct, and are you allowed in. Check them in that order and you won't waste an hour fixing permissions on a daemon that isn't even running. Every section below maps to exactly one of those three questions.

⚠ Don't Reinstall Docker First
Reinstalling replaces the client and the daemon binaries, but it won't fix a stale DOCKER_HOST, a wrong context, or group membership — those all live in your environment, not in the package. Diagnose before you reinstall.
📊 Production Insight
On-call engineers routinely burn an hour rebuilding images for what is a connectivity error. One team re-pulled 40GB of images across CI before someone ran docker version and saw the server section missing entirely.
🎯 Key Takeaway
The CLI is a client and dockerd is the server. docker info names the address it dialed — read that line first, then check listening, address, and permission in order.

dockerd Isn't Running: Start It on Linux, Mac, and Windows

The most common cause is also the simplest: dockerd isn't running. On Linux servers it usually means the service is stopped, crashed, or failed at boot — after an unattended upgrade, an OOM kill, or a daemon.json typo that prevents startup. On Mac and Windows it usually means Docker Desktop isn't launched, is still starting (the whale icon is animating), or is paused. Containers don't keep the daemon alive; the daemon keeps containers alive, so a dead daemon takes every docker command down with it.

On Linux, confirm with sudo systemctl status docker --no-pager: "active (running)" means look elsewhere, anything else means start it with sudo systemctl start docker and enable boot startup via sudo systemctl enable docker. If it refuses to start, read the real reason with sudo journalctl -u docker --since '15 min ago' — a JSON syntax error in /etc/docker/daemon.json is the classic silent killer, and journalctl prints the exact line. Validate the file with python3 -m json.tool /etc/docker/daemon.json before restarting.

On Docker Desktop, the daemon lives inside a VM and the socket is forwarded to ~/.docker/run/docker.sock. If the app isn't running, that socket simply doesn't exist. Launch the app, wait for the whale icon to stop animating, and confirm with ls ~/.docker/run/docker.sock. In CI, add a pre-flight docker info with a short timeout so a stopped daemon fails the job in seconds with a clear message instead of timing out 10 minutes into the build.

daemon-start-checks.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# Is the daemon alive? (Linux)
sudo systemctl status docker --no-pager
sudo systemctl start docker
sudo systemctl enable docker

# Why did it fail? Read the daemon's own logs
sudo journalctl -u docker --since '15 min ago' | tail -30

# A daemon.json typo kills startup silently — validate it
python3 -m json.tool /etc/docker/daemon.json

# Docker Desktop (Mac/Windows): socket exists only while the app runs
ls -l ~/.docker/run/docker.sock
ps aux | grep -i '[d]ocker desktop'

# Universal smoke test: server section present means connected
docker version
timeout 30 docker info
📊 Production Insight
After unattended upgrades, dockerd often fails back to stopped because the package post-install didn't restart it. A systemd enable plus a monitoring check on docker info catches this before Monday traffic does.
🎯 Key Takeaway
Check systemctl status, read journalctl on failure, validate daemon.json, and confirm Docker Desktop is actually launched — then prove it with docker version.

DOCKER_HOST Points Somewhere Wrong

DOCKER_HOST is an environment variable that overrides everything — contexts, defaults, all of it. Set it once for a remote builder session and forget to unset it, and every later docker command dials that old address: a decommissioned EC2 box, a builder VM you deleted, an SSH host whose key rotated. The error then looks like a network outage (TCP timeout, connection refused, SSH handshake failure) when it's really a leftover export in ~/.bashrc, /etc/environment, a direnv file, or a CI env block.

Diagnose in seconds: echo $DOCKER_HOST. Empty means the variable isn't your problem. A unix:// value should match a socket that exists on disk — check with ls -l on the path. A tcp:// value needs a reachable host and usually TLS certs via DOCKER_TLS_VERIFY and DOCKER_CERT_PATH; a connection refused means nothing listens there anymore. An ssh:// value needs working SSH credentials — test with plain ssh first, because the Docker CLI won't give you SSH-level diagnostics.

The fix is removal, not repair, when the target is retired: unset DOCKER_HOST in the current shell, then delete the export line from wherever it persists (shell rc files, /etc/environment, IDE run configs, pipeline YAML). If you genuinely use a remote daemon, prefer a named Docker context over a raw env var — contexts are visible in docker context ls and switchable, while env vars are invisible until they bite. Always re-verify with env | grep -i docker to prove nothing Docker-related lingers.

docker-host-cleanup.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# Where is the CLI dialing?
echo "DOCKER_HOST=$DOCKER_HOST"
env | grep -i docker

# If it's a stale remote, drop it (current shell)
unset DOCKER_HOST
unset DOCKER_TLS_VERIFY
unset DOCKER_CERT_PATH

# Find where it persists across shells
cat ~/.bashrc ~/.zshrc /etc/environment 2>/dev/null | grep -n DOCKER
# Delete the export line from whichever file above matches, then:
exec $SHELL -l
env | grep -i docker   # should print nothing

# Legit remote daemon? Prefer a visible context over env vars
docker context create builder --docker host=ssh://deploy@10.0.4.60
docker context use builder
docker info | head -8
📊 Production Insight
Stale DOCKER_HOST entries survive migrations because old machines keep old shell files. One team's 11-day-old builder IP lived on in /etc/environment and faked a network outage across 6 runners.
🎯 Key Takeaway
DOCKER_HOST overrides everything. Echo it, test the target independently, delete it if retired, and use named contexts instead of env vars for real remotes.

Socket Permissions: the docker Group and newgrp

On Linux the default socket /var/run/docker.sock is owned by root:docker with mode 660 — readable and writable only by root and members of the docker group. If your user isn't in that group, every command fails with "permission denied" or a dial error wrapping EACCES. This hits every new hire, every fresh VM, and every CI user that runs unprivileged. The trap: sudo docker ps works, so people conclude Docker is fine and their user config is broken — both true, and sudo-everything becomes the permanent workaround.

Fix it properly. Check the socket with ls -l /var/run/docker.sock and your membership with id -nG $USER. If docker is missing, run sudo usermod -aG docker $USER — the -a flag matters, since usermod -G without -a replaces all your groups and can lock you out of sudo. Then you must log out and back in, because group membership is evaluated at login. newgrp docker grants the group to the current shell only, which is fine for a quick test but evaporates when the shell closes — a classic reason the "fix" works in one terminal and fails in the next.

Understand the trade-off you're accepting: the docker group is effectively root-equivalent, since anyone who can talk to the daemon can mount / as a volume. That's fine for a personal dev box but worth a second thought on shared build machines, where rootless Docker or sudo-with-audit may fit better. Either way, never chmod 777 the socket — it silences today's error and hands every local user a root shell.

socket-permission-fix.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Inspect the door and your badge
ls -l /var/run/docker.sock
id -nG "$USER"

# Join the docker group (-a appends; without it you lose sudo)
sudo usermod -aG docker "$USER"

# Group membership is read at login — log out and back in, then:
id -nG "$USER"   # docker should now appear
docker ps          # must work WITHOUT sudo

# Quick test in the current shell only (does not persist)
newgrp docker <<'EOF'
docker ps
EOF
📊 Production Insight
Teams that paper over this with sudo docker accumulate root-owned files in ~/.docker and build caches, which then break the moment someone runs without sudo. Fix the group once instead of sudo-ing forever.
🎯 Key Takeaway
Socket is root:docker 660. Use usermod -aG, log out and back in, verify without sudo — and never chmod the socket.

Docker Contexts: the Remote Engine You Forgot

Docker contexts are named daemon endpoints — default for the local socket, desktop-linux for Docker Desktop, plus any remotes your team added for builders or staging hosts. The active context is sticky: switch to a remote for one deploy, and every docker command keeps dialing it until you switch back. When that remote gets deleted or its certs expire, you get daemon connection errors on a machine whose local daemon is perfectly healthy. docker context ls shows a star on the active entry; most people never look at it.

Run docker context ls the moment local checks pass but remote-style errors persist. Inspect the suspect with docker context inspect <name> and read the Host field under Endpoints. If it names a host that no longer exists, switch home with docker context use default on Linux (desktop-linux under Docker Desktop) and confirm docker info answers locally. Then clean up: docker context rm on every retired remote so the next engineer can't step on the same rake. Contexts live in ~/.docker/contexts, so a fix there follows your user across terminals — unlike env vars, there's exactly one place to look.

Make contexts boring infrastructure: name remotes after their purpose (prod-builder, staging), document the switch-back step in the runbook for any task that changes context, and prefer CI jobs that set context explicitly per step over relying on whatever the shared runner had active. A context-aware prompt segment (showing the active context in your shell) turns this whole failure class into something you see before it bites.

docker-context-rescue.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Which daemon is active? The star marks it
docker context ls

# Read the endpoint of the suspect context
docker context inspect prod-builder --format '{{json .Endpoints}}'

# Switch back to the local daemon
docker context use default        # Linux servers
# docker context use desktop-linux  # Docker Desktop

docker context ls                 # star should be on the local entry
docker info | head -8             # proves local answers

# Remove retired remotes so nobody selects them again
docker context rm prod-builder staging-builder
📊 Production Insight
Sticky contexts cause the weirdest tickets: Docker works for one user and fails for another on the same box, because contexts are per-user under ~/.docker. Always ask which context is active before touching the daemon.
🎯 Key Takeaway
Contexts are sticky and per-user. List them, inspect the Host, switch home, delete retired ones, and show the active context in your prompt.

Prove It's Fixed and Keep It Fixed

A fix you can't prove is a guess. Run the verification ladder: docker version shows both client and server sections, docker info prints storage driver and runtimes, and docker run --rm hello-world completes a full pull-and-run cycle against the daemon you intend to use. Each step exercises a deeper layer — dial, API, and actual container lifecycle — so passing all three means the connection is genuinely healthy, not just momentarily quiet. Record which daemon answered (docker info --format '{{.Name}}') so a passing test against the wrong engine doesn't fool you.

Then lock it in. On servers, systemctl enable docker plus a monitoring probe that runs docker info on a schedule catches the stopped-daemon case before users do. In CI, a pre-flight step with timeout 30 docker info fails fast with the resolved address in logs, turning future incidents into one-line diagnoses. For fleets, manage DOCKER_HOST and contexts through config management rather than shell files, and re-image long-lived runners after any migration so stale exports can't outlive the machines they were meant for.

Finally, write down your topology. A three-line note — where the daemon runs, which context is standard, who owns the remote builders — saves every future on-call from rediscovering it at 8 AM. The daemon connection is the front door of your whole container workflow; treat it like infrastructure with an owner, a monitor, and a runbook, and this error drops from incident to footnote.

daemon-verify-harden.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
# Three-layer proof: dial, API, full container lifecycle
docker version
docker info --format 'daemon={{.Name}} driver={{.Driver}}'
docker run --rm hello-world | head -5

# CI pre-flight: fail fast with the resolved address in logs
echo "daemon target: ${DOCKER_HOST:-default socket / active context}"
timeout 30 docker info > /dev/null || { echo 'daemon unreachable'; exit 1; }

# Server hardening: start on boot + scheduled probe
sudo systemctl enable docker
# cron: */5 * * * * /usr/bin/docker info >/dev/null 2>&1 || echo 'dockerd down' | mail -s 'docker alert' ops@example.com
💡Log the Daemon Address in CI
Echo DOCKER_HOST and the active context at the top of every pipeline that uses Docker. When the address goes stale, the log names the culprit instead of showing a bare timeout.
📊 Production Insight
The teams that stop seeing this error all share one habit: a 30-second docker info pre-flight in CI and a daemon probe in monitoring. Both are five-minute setups that convert incidents into self-diagnosing logs.
🎯 Key Takeaway
Verify with version, info, and a hello-world run. Then add boot enablement, a probe, and a CI pre-flight so the next failure announces its cause.
● Production incidentPOST-MORTEMseverity: high

A Stale DOCKER_HOST Parked 34 CI Builds for 52 Minutes

Symptom
At 8:04 AM on a Monday, all 6 self-hosted GitHub Actions runners started failing every Docker step with "Cannot connect to the Docker daemon at tcp://10.0.4.51:2376." Thirty-four builds queued in 20 minutes. The runners themselves were green, disk and CPU were normal, and dockerd was running fine on each machine. Engineers assumed a network outage and paged the platform team.
Assumption
The team assumed the VPC route to the builders had broken over the weekend, because the error named an IP nobody on call recognized. Two engineers spent 25 minutes checking security groups and NACLs. The real story: 11 days earlier the team had migrated from a remote builder at 10.0.4.51 to building locally on each runner, and the migration PR removed the DOCKER_HOST export from the runner image template — but 6 long-lived runners had never been re-imaged, so the stale export in /etc/environment survived.
Root cause
DOCKER_HOST=tcp://10.0.4.51:2376 was still exported in /etc/environment on the 6 old runners, overriding the local socket. The old builder had been terminated, so every docker CLI call dialed a dead IP until the TCP timeout. New runners built from the updated template worked fine, which is why the failure hit only a subset of the fleet and looked like a network partition instead of a config leftover.
Fix
Immediate: SSH to each of the 6 runners, delete the DOCKER_HOST line from /etc/environment, and restart the runner service — builds recovered within 9 minutes of the first fix. Same day: re-imaged all runners from the current template and added a pipeline pre-flight step that runs docker info with a 30-second timeout and fails fast with the resolved daemon address in the log. That week: moved the builder address into the orchestrator's config management so no static IP ever lives in a shell file again.
Key lesson
  • Stale environment beats broken infrastructure as a suspect. When the error names an address, check who configured that address before you check the network path to it — env | grep -i docker takes 2 seconds and would have saved 25 minutes.
  • Long-lived runners drift from their templates. Any migration that changes connection config must force a re-image or reboot of the whole fleet, not just update the template for future machines.
  • Log the resolved daemon address on every CI Docker step. A pre-flight docker info turns a mystery timeout into a one-line diagnosis the next time an address goes stale.
Production debug guideFive symptoms, five exact command sequences — run them in order and you'll land on the cause.5 entries
Symptom · 01
Every docker command fails, and you don't know whether the daemon or your config is at fault
Fix
Ask the CLI where it's dialing and whether anyone answers: run docker info first — its error names the exact address. Then run docker context ls to see the active context (a star marks it) and echo $DOCKER_HOST to see any override. If DOCKER_HOST is set, it wins over the context. If both are empty, the CLI uses the default socket. This 30-second triage tells you which of the four causes to chase.
Symptom · 02
The address looks right (local socket) but nothing answers — suspect dockerd isn't running
Fix
Check the daemon on Linux with sudo systemctl status docker --no-pager and sudo journalctl -u docker --since '15 min ago' | tail -30 for crash loops or disk errors. On Mac or Windows, check that Docker Desktop is running — ps aux | grep -i docker | grep -v grep should show com.docker processes, and ls -l ~/.docker/run/docker.sock should exist. Start it with sudo systemctl start docker or by launching the Desktop app.
Symptom · 03
DOCKER_HOST names a TCP or SSH address and dials time out
Fix
Print it with echo $DOCKER_HOST and decide if that machine should exist: try ssh with the same target (ssh user@host true) or curl the TLS endpoint. If the host is retired, run unset DOCKER_HOST in the shell and delete the export from ~/.bashrc, ~/.zshrc, /etc/environment, or the CI env config — whichever re-injects it. Verify with env | grep -i docker showing nothing, then docker info succeeding locally.
Symptom · 04
Error says permission denied on /var/run/docker.sock
Fix
Inspect the door with ls -l /var/run/docker.sock (expect srw-rw---- root docker) and check your badge with id -nG $USER. If docker isn't listed, run sudo usermod -aG docker $USER, then fully log out and back in — group membership is read at login, so newgrp docker only helps the current shell. Confirm with docker ps running without sudo.
Symptom · 05
Local daemon runs and perms are fine, but the CLI still dials a remote engine
Fix
List contexts with docker context ls and inspect the starred one via docker context inspect --format '{{json .Endpoints}}'. If it points at a dead remote, switch back with docker context use default (or desktop-linux under Docker Desktop) and confirm with docker context ls showing the star on the local entry. Delete retired remotes with docker context rm <name> so nobody selects them again.
Daemon Connection Failures — Confirm, Fix, Prevent
Root CauseHow to ConfirmFixPrevention
dockerd not runningsudo systemctl status docker shows inactive; Docker Desktop app closed; docker version shows client onlysudo systemctl start docker (Linux) or launch Docker Desktop; sudo systemctl enable dockerEnable on boot; monitor with a scheduled docker info probe; CI pre-flight check
Stale or wrong DOCKER_HOSTecho $DOCKER_HOST names a dead TCP/SSH host; env | grep -i docker shows leftoversunset DOCKER_HOST and delete the export from shell rc, /etc/environment, or CI envManage builder addresses in config management; re-image runners after migrations
Socket permission deniedls -l /var/run/docker.sock is root:docker 660; id -nG lacks docker; sudo docker workssudo usermod -aG docker $USER, then full logout and login; verify without sudoProvision users into the group at account creation; never chmod the socket
Wrong active Docker contextdocker context ls star sits on a retired remote; inspect shows a dead Hostdocker context use default (or desktop-linux); docker context rm the retired remoteDocument context switches in runbooks; show active context in shell prompt
Corrupt daemon.json or dead Desktop VMjournalctl -u docker shows JSON parse error; Desktop socket path missing entirelyValidate with python3 -m json.tool, fix syntax, restart daemon or Desktop appLint daemon.json in config management; pin Desktop resource settings per team guide
⚙ Quick Reference
5 commands from this guide
FileCommand / CodePurpose
daemon-start-checks.shsudo systemctl status docker --no-pagerdockerd Isn't Running
docker-host-cleanup.shecho "DOCKER_HOST=$DOCKER_HOST"DOCKER_HOST Points Somewhere Wrong
socket-permission-fix.shls -l /var/run/docker.sockSocket Permissions
docker-context-rescue.shdocker context lsDocker Contexts
daemon-verify-harden.shdocker versionProve It's Fixed and Keep It Fixed

Key takeaways

1
docker info names the dialed address
read it before touching anything else.
2
Stopped daemon
systemctl start plus journalctl on Linux; launch Desktop on Mac and Windows.
3
Stale DOCKER_HOST overrides everything
echo it, test the target, delete it if retired.
4
Socket 660 means group membership
usermod -aG docker, full re-login, verify without sudo.
5
Sticky contexts dial dead remotes
list, switch home, delete retired ones.
6
Prove with version, info, and hello-world; prevent with boot enablement, probes, and CI pre-flights.

Common mistakes to avoid

6 patterns
×

Reinstalling Docker before diagnosing

Symptom
Fresh binaries, same error — plus lost images and an hour gone, because DOCKER_HOST, contexts, and group membership all survived the reinstall.
Fix
Run docker info, echo $DOCKER_HOST, and docker context ls first. Reinstall only when journalctl proves the daemon binary itself is broken.
×

Using sudo as the permanent workaround

Symptom
Commands work with sudo but fail without it forever; root-owned files pile up in ~/.docker and caches, breaking later non-sudo runs.
Fix
Join the docker group once with sudo usermod -aG docker $USER, log out and back in, and verify docker ps without sudo.
×

Testing the fix with newgrp and calling it done

Symptom
Works in the current terminal, fails in every new one — newgrp grants the group to one shell only, not to the user.
Fix
Treat newgrp as a test, not a fix. Log out and back in for the real membership, then open a fresh terminal and verify.
×

Setting DOCKER_HOST for one task and forgetting it

Symptom
Weeks later every local command dials a long-dead remote builder; the team debugs the network instead of the shell.
Fix
unset DOCKER_HOST when done, delete persisted exports, and prefer named docker contexts for remote work.
×

Editing daemon.json without validating JSON

Symptom
Daemon refuses to start after a config tweak; systemctl shows failed with a cryptic exit code and no containers run.
Fix
Validate every edit with python3 -m json.tool /etc/docker/daemon.json, then restart and tail journalctl -u docker.
×

Switching context for a deploy and never switching back

Symptom
Local daemon healthy, but all commands fail against a retired remote — confusing anyone else who uses the machine.
Fix
End every remote-context task with docker context use default, and remove retired remotes with docker context rm.
INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01JUNIOR
You run docker ps and get Cannot connect to the Docker daemon. What's yo...
Q02JUNIOR
docker ps fails for your user but sudo docker ps works. What's wrong and...
Q03SENIOR
How does DOCKER_HOST interact with Docker contexts, and which wins?
Q04SENIOR
dockerd won't start after you edited /etc/docker/daemon.json. How do you...
Q05SENIOR
Half your CI fleet fails to reach the daemon while the rest passes. How ...
Q01 of 05JUNIOR

You run docker ps and get Cannot connect to the Docker daemon. What's your first command and why?

ANSWER
docker info, because its error names the exact address the CLI dialed — socket path, TCP host, or SSH target. That one line tells me whether I'm fighting a stopped local daemon, a stale DOCKER_HOST, or a wrong context, so I check the right thing instead of guessing.
FAQ · 6 QUESTIONS

Frequently Asked Questions

01
Is the daemon running if docker ps fails but the containers are still up?
02
Why does Docker work in one terminal but not another?
03
Do I need to reboot after joining the docker group?
04
Can a full disk cause this error?
05
Why does Docker Desktop show this error right after my laptop wakes up?
06
Should I use DOCKER_HOST or a context for a remote builder?
N
Naren Founder & Principal Engineer

20+ years shipping production infrastructure and CI/CD at scale. Drawn from code that ran under real load.

Follow
Verified
production tested
September 23, 2026
last updated
1,905
articles · all by Naren
🔥

That's Docker. Mark it forged?

6 min read · try the examples if you haven't

Previous
APT Unable to Locate Package Fix
21 / 24 · Docker
Next
OCI Runtime Create Failed Fix