Add GitLab Runner to the Docker Group: Practical, Maintainable Permissions
Encountering this during a CI run?
Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Get http:///var/run/docker.sock/v1.24/images/json: dial unix /var/run/docker.sock: connect: permission denied
Root access, sudo
, or chowning /var/run/docker.sock
may “solve” the error. Each introduces risk. In multi-user environments—or any regulated infrastructure—privilege escalation creates audit and attack surface headaches.
Instead, align with Docker’s least privilege guidance: add the GitLab Runner user to the docker
group. Offload container daemon access to a supplementary group, which streamlines permission boundaries.
Why Use the Docker Group for GitLab Runner?
Approach | Security | Future Maintenance | Auditability |
---|---|---|---|
Run as root | Worst | Poor | Opaque |
Sudo in jobs | Moderate | Friction | Diffuse |
docker group | Targeted | Clean | Traceable |
- Running unprivileged users with Docker group access shrinks the attack surface.
- Granting only what’s necessary preserves traceability.
- No need to sprinkle
sudo
across dozens of.gitlab-ci.yml
files.
Note: All group members still control the Docker daemon—a well-known caveat. On hardened multi-tenant hosts, consider separate daemons or socket proxies.
Step-by-Step: Assign Docker Group Membership to GitLab Runner
Some sites run vanilla GitLab Runner, others deploy via Helm chart or as a systemd service. The following reference assumes gitlab-runner
user (default install on Ubuntu 20.04+ or similar).
1. Verify Docker and Group Setup
Ensure Docker Engine ≥ 20.10 is running:
sudo systemctl status docker
docker version
Check the docker
group exists and your user is part of it:
getent group docker
groups $USER
If gitlab-runner
is absent, continue.
2. Identify GitLab Runner Execution Context
Find the Runner’s service user:
systemctl status gitlab-runner | grep 'User='
ps aux | grep gitlab-runner
Typical output:
User=gitlab-runner
On distributions without explicit User=
, use ps
to check.
3. Assign Docker Group
sudo usermod -aG docker gitlab-runner
The -aG
flags ensure existing groups are preserved.
Known Issue: Group membership is only picked up by new login sessions. Systemd does not always auto-refresh—see below.
4. Bounce GitLab Runner Service
Force session reload:
sudo systemctl restart gitlab-runner
For shell executors, a log-out/log-in cycle is mandatory.
Check:
id gitlab-runner
Expected: docker
listed in groups.
5. Pipeline Test: Docker Daemon Access
Add to .gitlab-ci.yml
:
docker-access-check:
image: docker:24.0.7
script:
- docker info
Result: job logs should contain daemon details (number of containers, storage driver, etc.)—not permission errors.
Common Pitfalls/Edge Cases
- Custom Docker socket location: If you run Docker with a custom socket (e.g.,
/run/docker.sock
), adjust bind mounts in the Runner config. - SELinux/AppArmor interference: On hardened hosts, MAC policies may still block docker daemon access despite Unix group membership. Check audit logs if permission issues persist.
- Group refresh delay: If
groups gitlab-runner
omitsdocker
after all steps, the systemd service may cache group info. Workaround: fully log out sessions or forcibly restart affected services/users. - Kubernetes executor: The above applies to shell or systemd runners; with k8s, Docker socket access is handled through volume mounts and pod security policies—not group assignments.
Gotcha: This method gives the Runner full control over the Docker daemon, including host access via volume mounts. For highly sensitive workloads, prefer rootless Docker or isolated build environments.
Why Not Sudo?
Permitting sudo docker
(even with NOPASSWD) in CI risks lateral privilege escalation. It also increases ambiguity: Does a failed docker
command mean a permissions error or a deeper misconfiguration?
Group-based access is explicit, easier to audit (/etc/group
), and easily reversed.
Example: Real-World CI Failure
A broken pipeline, pre-fix:
$ docker build -t myimage .
Got permission denied while trying to connect to the Docker daemon socket...
Post-fix:
$ docker info
Client:
Context: default
Server:
Containers: 8
...
Add the group, restart services, re-run the job—no more hunting for stray sudo
prompts.
Alternate Approaches (Not Used Here)
docker:dind
images enable self-contained DinD, but complicate caching, storage, and networking.- Rootless Docker offers better isolation—requires additional config and still maturing for all cases.
Summary:
Direct group assignment is a pragmatic, well-understood fix for runner permission errors against Docker’s root-owned Unix socket.
Configure once, automate for new runners, and review access as your environment evolves.
If stuck, inspect runner logs (/var/log/gitlab-runner/
), audit group memberships, and scan Docker daemon logs for additional permission denials.
Questions or edge-case findings? Raise a merge request or discuss with your sysadmin team.
No silver bullets—but this gets most teams >95% to friction-free Docker-powered CI/CD.