What is the Unix socket at /var/run/docker.sock, and what does a container gain when you bind-mount that file into it?
answer
- dockerd is root; CLI is just an HTTP client
- Unix socket file, root:docker 0660
- curl --unix-socket needs no CLI
- mounted socket = sibling containers, host paths
- :ro only protects the file, not the API
basics
~20 sIt is the Unix socket the root Docker daemon listens on for its REST API. Bind-mounting it lets a container send API calls to the host daemon (run, build, exec, mount, inspect), so it can control every container on that host.
solid answer
~40 sThe `docker` CLI is a thin HTTP client; the real work is done by `dockerd`, a root daemon. By default that API listens on a Unix domain socket, the file `/var/run/docker.sock`, governed by filesystem permissions (`root:docker`, mode 0660). Anything that can open it can speak the Engine API: `curl --unix-socket /var/run/docker.sock http://localhost/containers/json` needs no Docker CLI at all. Bind-mounting the socket into a container puts that file inside the container's filesystem. No nested daemon is created; containers started from inside are **siblings**, created by the host daemon, and every `-v` path is resolved against the **host** filesystem. Tooling does it constantly: Traefik, Portainer, cAdvisor, Watchtower, Testcontainers, CI runners. Note that `:ro` on the mount only stops the container overwriting the socket file; it does not make the API read-only.
code
bash · 3 linescurl --unix-socket /var/run/docker.sock http://localhost/v1.44/containers/json
ls -l /var/run/docker.sock # srw-rw---- root docker
docker run -v /var/run/docker.sock:/var/run/docker.sock some/toolgo deeper
Know that the socket is how the CLI talks to the root daemon, and mounting it gives a container control of Docker on that host.
Explain sibling containers, host-path resolution, and that :ro does not restrict the API.
Frame it as an attack-surface decision: which workloads legitimately need it, and reach for a proxy or rootless alternative otherwise.
Set an org policy: socket access is root-equivalent, so it belongs only to trusted control-plane components with mediated, least-privilege access.
## The Docker client is not Docker The `docker` command you type is a thin HTTP client. The real work (pulling images, creating containers, mounting volumes, wiring networks) is done by a long-running daemon, `dockerd`, which runs as **root** on the host. The CLI serialises your command into a request against the Docker Engine API and prints the response. ## What the socket is By default `dockerd` listens on a **Unix domain socket** exposed as the file `/var/run/docker.sock`. A Unix socket is an inter-process communication endpoint that looks like a file, so ordinary filesystem permissions decide who may talk to it. On a stock install it is owned `root:docker` with mode `0660`: root and members of the `docker` group can connect; nobody else. That is exactly why 'add yourself to the docker group' is equivalent to giving yourself passwordless root. Because it is just an HTTP API over that socket, you do not need the Docker CLI to use it. `curl --unix-socket /var/run/docker.sock http://localhost/v1.44/containers/json` lists containers directly. The CLI is convenience; the socket is the power. ## What mounting it into a container does When you run `-v /var/run/docker.sock:/var/run/docker.sock`, you place the host's socket file inside the container. A process in the container can now issue Engine API calls to the **host** daemon. Crucially this does **not** start a second daemon inside the container. Any containers it launches are created by the host `dockerd` and are therefore **siblings** of the mounting container, sharing the same host. This is the 'Docker-outside-of-Docker' or socket-mount pattern. Two consequences trip people up: - **Paths are host paths.** If code inside the container asks the API to bind-mount `/data`, that `/data` is resolved on the host, not in the container. This is the classic 'my volume is empty' bug in CI. - **`:ro` is nearly useless for safety.** Marking the mount read-only only prevents replacing the socket *file*; the API behind it still accepts create/exec/mount calls. It is not a read-only API. ## Why so much tooling wants it Reverse proxies (Traefik, Caddy), management UIs (Portainer), metrics agents (cAdvisor), auto-updaters (Watchtower), and integration-test libraries (Testcontainers) all read or drive the daemon, so they ask for the socket. That ubiquity is the danger: it normalises handing arbitrary containers full host control. The security follow-on (why the socket equals root, and how to reduce the exposure) is covered in the escape and socket-proxy questions.
- Does mounting the socket start a Docker daemon inside the container?No. There is still exactly one daemon, the host's `dockerd`. The container just gets a channel to it, so any containers it launches are siblings created on the host, sharing the host kernel and filesystem namespace for volume resolution.
- Why does adding a user to the docker group count as granting root?The `docker` group owns the socket at mode 0660, so group members can call the Engine API. That API can run a container as root with the host filesystem mounted, which is trivially full root. There is no privilege boundary between 'docker group' and 'root'.
The socket is the keyhole to the daemon's front door. The CLI is one key shape, but anyone who can reach the keyhole can pick it with curl.
saying these in an interview costs you the question
- Thinking `dockerd` runs per-container rather than one root daemon per host
- Believing `:ro` on the socket mount makes the API read-only
- Assuming volume paths passed via the mounted socket resolve inside the container
- Treating the docker group as a lesser privilege than root