Docker permission denied on docker.sock: the Right Fix (and Its Security Cost)
Symptom: works with sudo, denied without it
permission denied while trying to connect to the Docker daemon socket at
unix:///var/run/docker.sock: Head "http://%2Fvar%2Frun%2Fdocker.sock/_ping":
dial unix /var/run/docker.sock: connect: permission denied
One-line diagnosis: the docker.sock socket file belongs to root:docker, and your user is neither root nor in the docker group. Docker is client/server: the CLI connects to the socket as your user, and the daemon-side check isn't about images or registries — it's "are you allowed to touch this socket".
Confirm the ownership:
ls -l /var/run/docker.sock
# srw-rw---- 1 root docker 0 ... /var/run/docker.sock
# ↑ owner root, group docker, no access for anyone else
The fix: add your user to the docker group (one step)
sudo usermod -aG docker $USER
newgrp docker # effective in the current terminal immediately, no logout
docker ps # try without sudo
newgrp only affects the current shell; for a full effect, log out and back in (group membership loads at login — su - $USER also works). Never drop the -a flag — -G docker without -a replaces your entire group list, and losing the sudo group locks you out of your own machine.
Three wrong fixes to avoid
1. chmod 777 /var/run/docker.sock
It works — and hands control of the daemon to every user on the machine. The socket gets recreated on daemon restart, reverting the permission, so the problem "mysteriously returns". Don't.
2. Permanent sudo docker ...
Works, with two costs: every docker artifact (volume data, build cache) defaults to root ownership, which your non-sudo scripts then can't read; and sudo sprinkled through CI scripts is harder to audit, not easier.
3. chown $USER /var/run/docker.sock
Same nature as 777 — you're changing the ownership of a system component, and it reverts on restart/upgrade.
The security cost you must know: docker group ≈ root
Joining the docker group grants an equivalent of passwordless root:
# Any docker-group user can get a root shell on the host like this
docker run -v /:/host -it alpine chroot /host sh
So: on a personal dev machine, joining the group is fine. On a multi-user production server, treat docker group grants with the same approval process as sudo. Safer alternatives:
- Rootless Docker: the daemon runs under your own user, the socket lives in
~/.config/docker— no permission problem, no privilege escalation (tradeoff: some features need extra setup, like binding ports below 1024); - Fine-grained access: a docker socket proxy (e.g. tecnativa/docker-socket-proxy) exposing only whitelisted API calls.
Neighboring errors that confuse people
| Error | Meaning | Direction |
|---|---|---|
| permission denied ... docker.sock | socket exists, you lack access | this post |
| Cannot connect to the Docker daemon | socket missing / daemon down | daemon post |
| permission denied (during pull) | registry auth failure | docker login, not this post |
The third one is the most misleading: it also says permission denied, but during docker pull — that's a private registry login issue, unrelated to socket permissions.
Rootless Docker and the socket-less future
The cleanest fix is not granting socket access but eliminating it: rootless Docker (dockerd-rootless-setuptool.sh install) runs the daemon as your user — no socket group, no root escalation path, the permission error class simply disappears. Trade-offs: some volume mounts and iptables networking behave differently, and performance of file-heavy bind mounts takes a hit (FUSE overhead). For CI and dev machines where you control the environment, rootless is increasingly the default recommendation; for shared servers, the group route with the audit habit below remains standard.
The audit habit if you must use the socket group
Adding a user to the docker group is root-equivalent (any member can mount the host filesystem into a container). Treat it like sudo: named users only, never service accounts, and periodically getent group docker plus a review of who still needs it. If a tool needs Docker access but full group membership feels excessive, the socket-proxy pattern (tecnativa/docker-socket-proxy) exposes a filtered API — allow containers/json and containers/create, deny images/build with host mounts — the principle-of-least-privilege version of the socket.
Docker-in-Docker vs socket-mounting: choosing deliberately
Two ways to give CI containers Docker, opposite failure modes: socket-mounting (-v /var/run/docker.sock:/var/run/docker.sock) shares the host daemon — fast, cache-friendly, but the "inner" containers are siblings, not children (cleanup scripts must know), and the security surface is the whole daemon. True DinD (--privileged inside) isolates properly but needs --privileged and eats disk for nested layers. The mistake to avoid: socket-mounting into build containers that run untrusted code — that is remote-code-execution-as-root on the host, wrapped in a container.
WSL2 and Docker Desktop: two sockets
On Windows + WSL2, docker inside a distro may talk to Docker Desktop’s relay rather than a classic /var/run/docker.sock owned by group docker. Symptoms: permission denied even after usermod, or a sock path under /mnt/wsl/.... Fix path: enable WSL integration for that distro in Docker Desktop, restart the distro, and confirm docker context show points at desktop-linux (or your intended context). Linux-native daemon advice in this article still applies on bare-metal / VM hosts without Desktop.
Checklist
ls -l /var/run/docker.sock— confirm group ownership is dockerid— does the group list include docker?- Not there →
sudo usermod -aG docker $USER && newgrp docker - In the group but still failing → log out and back in (groups refresh at login)
- Server scenario → read the "security cost" section before deciding
- Never use the chmod 777 / chown / permanent-sudo trap fixes
- WSL2 + Desktop → verify integration and
docker contextbefore chasing group membership
CI agents: group membership is not enough
Build agents often run as a service user that never “logs in,” so usermod -aG docker alone never refreshes supplementary groups for the long-lived process. Prefer: restart the agent after group changes, or run Docker via a rootless user systemd unit, or use a dedicated DinD service with explicit credentials. Document which pattern the fleet uses — mixing socket mounts and DinD on the same runner is how “works on my laptop” CI flakes appear.
Triage notes
The fix is a single line of usermod, yet the wrong fixes get three sections — chmod 777, permanent sudo, and chown all "work", but share the same flaw: the daemon rebuilds the socket on restart and the permissions revert, returning as an "unexplained relapse"; permanent sudo additionally leaves every docker artifact owned by root. The security-cost section deliberately follows the fix: docker group membership is equivalent to passwordless root — the article's command mounting the host root filesystem is the proof — and on multi-user servers it deserves the same approval workflow as sudo. usermod's -a is marked "never drop it" because -G without -a removes you from every other group, and losing the sudo group locks you out.
Provided by ToolVault. Related tools: Dockerfile Generator, Run to Compose, Linux Cheatsheet, chmod Calculator (two-way rwx ↔ 755 conversion when the numbers escape you). Related reading: Docker daemon unreachable, SSH Permission denied (publickey). 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.