1. Introduction
You've set up a Jenkins pipeline to build Docker images, and it fails immediately with an error like this:
Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Post "http://%2Fvar%2Frun%2Fdocker.sock/v1.24/build": dial unix /var/run/docker.sock: connect: permission denied
This is one of the most common CI/CD setup problems on Jenkins, and it has several valid fixes depending on how your Jenkins is deployed. After fixing Docker access, if your containerised application starts crashing, see Fix Kubernetes CrashLoopBackOff. The wrong fix can introduce a real security risk — so this guide covers not just how to make it work, but which approach is appropriate for your setup.
2. What This Error Means
Docker communicates with its daemon through a Unix socket: /var/run/docker.sock. By default, that socket is owned by root and the docker group, with permissions that look like this:
ls -la /var/run/docker.sock
# srw-rw---- 1 root docker 0 Apr 21 09:14 /var/run/docker.sock
The jenkins user running your pipeline doesn't belong to the docker group, so it can't read or write to the socket. The Docker CLI can't talk to the daemon, and every Docker command fails immediately.
There are three distinct deployment scenarios where this error appears, and each has a different correct fix:
- Jenkins installed directly on a Linux host (bare metal or VM)
- Jenkins running in a Docker container with the Docker socket mounted into it
- Jenkins agents running in Kubernetes pods
3. Common Causes
- The jenkins OS user is not a member of the docker group on the host
- Jenkins is running in a container and the host Docker socket is not mounted into it
- The socket is mounted but the jenkins user inside the container has a different UID from the host docker group
- Jenkins was restarted after adding the user to the docker group but the session didn't pick up the new group membership
- A Jenkins agent container was built without adding its user to the docker group
- Kubernetes-based agents have no Docker daemon available at all on the node (containerd/CRI-O cluster)
4. Step-by-Step Fix
Step 1: Identify your Jenkins deployment type
Before applying any fix, confirm how Jenkins is running. This determines everything:
# Check if Jenkins is running as a system service (bare metal / VM install) systemctl status jenkins
# Check if Jenkins is running as a container docker ps | grep jenkins
# Check the user Jenkins pipelines run as
# Add this shell step to a test pipeline: sh 'whoami && id'
Step 2 (Bare metal / VM): Add the jenkins user to the docker group
This is the correct fix for Jenkins installed directly on a Linux host. The jenkins user needs to be a member of the docker group to access the socket:
# Add jenkins user to the docker group sudo usermod -aG docker jenkins
# Verify the change id jenkins
# uid=115(jenkins) gid=121(jenkins) groups=121(jenkins),999(docker)
# IMPORTANT: restart Jenkins to apply the new group membership
# The change does NOT take effect until Jenkins is restarted sudo systemctl restart jenkins
Verify it worked by running a test pipeline with:
sh 'docker info'
Step 3 (Containerised Jenkins): Mount and configure the Docker socket
If Jenkins itself is running in a Docker container, the standard approach is to bind-mount the host's Docker socket into the container. This lets the Jenkins container use the host's Docker daemon:
# docker run example with socket mounted docker run -d \ --name jenkins \ -p 8080:8080 \ -v /var/run/docker.sock:/var/run/docker.sock \ -v jenkins_home:/var/jenkins_home \ jenkins/jenkins:lts
# After starting the container, install Docker CLI inside it
# and add the jenkins user to the docker group: docker exec -it -u root jenkins bash apt-get update && apt-get install -y docker.io
# Get the GID of the docker group on the HOST
# (run this on the host, not inside the container) getent group docker | cut -d: -f3
# e.g. 999
# Inside the container: add jenkins user to a group with that GID groupmod -g 999 docker # or: groupadd -g 999 docker usermod -aG docker jenkins
If you manage this with Docker Compose, the cleaner approach is to set it in your compose file:
# docker-compose.yml services: jenkins: image: jenkins/jenkins:lts user: root # or a specific UID:GID volumes: - /var/run/docker.sock:/var/run/docker.sock - jenkins_home:/var/jenkins_home ports: - "8080:8080"
# Running as root avoids the GID problem but has broader security implications.
# See the security section below for the right tradeoff.
Step 4 (Kubernetes agents): Choose the right approach
Kubernetes clusters typically run containerd or CRI-O as the container runtime — there is no Docker daemon on the node for Jenkins agents to connect to. You have three realistic options:
Option A: Docker-in-Docker (DinD) sidecar
# Jenkinsfile — Kubernetes agent with DinD sidecar pipeline { agent { kubernetes { yaml '''
apiVersion: v1
kind: Pod spec: containers: - name: jnlp image: jenkins/inbound-agent:latest - name: docker image: docker:24-dind securityContext: privileged: true env: - name: DOCKER_TLS_CERTDIR value: "" volumeMounts: - name: docker-storage mountPath: /var/lib/docker volumes: - name: docker-storage emptyDir: {} ''' } } stages { stage('Build') { steps { container('docker') { sh 'docker build -t myapp:latest .' } } } } }
Option B: Kaniko (no Docker daemon required)
# Jenkinsfile — Kaniko build in Kubernetes pipeline { agent { kubernetes { yaml '''
apiVersion: v1
kind: Pod spec: containers: - name: kaniko image: gcr.io/kaniko-project/executor:latest command: ["/busybox/cat"] tty: true ''' } } stages { stage('Build & Push') { steps { container('kaniko') { sh ''' /kaniko/executor \ --context=. \ --dockerfile=Dockerfile \ --destination=myregistry/myapp:${BUILD_NUMBER} ''' } } } } }
5. Verification Steps
After applying your fix, run the following verification checks before testing a full pipeline build:
Bare metal / VM
# Confirm jenkins is in the docker group id jenkins
# Expected: groups=...,999(docker)
# Test Docker access as the jenkins user sudo -u jenkins docker info sudo -u jenkins docker ps
# If the above works, run a test pipeline stage: sh 'docker version'
Containerised Jenkins
# From inside the Jenkins container, verify socket access docker exec -it -u jenkins jenkins docker info
# Confirm the socket is mounted and its GID matches the host docker exec -it jenkins ls -la /var/run/docker.sock
# srw-rw---- 1 root 999 0 Apr 21 09:14 /var/run/docker.sock
# GID 999 should match the host's docker group GID getent group docker # run this on the host
Kubernetes agents
# Create a minimal test pipeline that runs docker info
# in the docker/DinD container sidecar: container('docker') { sh 'docker info' sh 'docker version' }
# For Kaniko, verify the executor runs without errors: container('kaniko') { sh '/kaniko/executor --help' }
6. Approaches at a Glance
Here's a comparison of every fix approach so you can choose the right one for your environment:
| Approach | How it works | Best for | Tradeoff |
|---|---|---|---|
| Add jenkins to docker group | Jenkins user gets read/write access to /var/run/docker.sock | Simple single-node Jenkins setups | Grants root-equivalent host access to Jenkins jobs |
| Docker socket bind mount | Mount host socket into Jenkins agent container | Containerised Jenkins agents | Same security concern — socket access = host root |
| Docker-in-Docker (DinD) | Run a separate Docker daemon in a privileged container | Kubernetes-based Jenkins agents, CI isolation | Privileged container required; slower builds |
| Rootless Docker | Docker daemon runs as non-root user | Security-conscious environments | More complex setup; some features limited |
| Kaniko / Buildah | Build images without a Docker daemon at all | Kubernetes-native CI, high-security environments | Different syntax; not a drop-in replacement |
For most teams running Jenkins on a VM or bare metal, adding the jenkins user to the docker group is the pragmatic choice. For Kubernetes-based CI, Kaniko is the most robust long-term option.
7. Security Considerations
Practical mitigations if you're using socket mounting:
- Restrict who can create or modify Jenkins pipelines (Jenkinsfiles)
- Use Jenkins folder-level permissions to limit which teams can run Docker builds
- Consider a Docker socket proxy (e.g., Tecnativa/docker-socket-proxy) that restricts which API endpoints are accessible
- Audit your Jenkins agents and remove Docker access from agents that don't need it
- For high-compliance environments, use Kaniko or Buildah to eliminate the Docker socket dependency entirely
8. Common Mistakes
- Not restarting Jenkins after adding the jenkins user to the docker group — this is the single most common reason the fix appears to not work
- Adding the user to the docker group inside the container but not matching the host GID — the socket permission will still be denied
- Running Jenkins container as root permanently instead of fixing the underlying permission — works but bypasses all container user isolation
- Using DinD without the privileged security context — DinD requires a privileged container; it will fail silently or with cryptic errors without it
- Assuming the fix worked without restarting the Jenkins service — test with a pipeline run, not just a shell command as your own user
- Mounting the Docker socket in Kubernetes on a containerd cluster and wondering why it doesn't work — there is no dockerd socket to mount
9. Prevention Tips
- Document your Jenkins agent setup — which user runs pipelines, whether Docker is available, and which method is used
- Use infrastructure-as-code (Ansible, Terraform, cloud-init) to configure the jenkins user's group membership on new nodes automatically
- For containerised Jenkins, bake the correct GID configuration into your custom Jenkins Docker image rather than setting it at runtime
- If using Kubernetes agents, standardise on Kaniko or Buildah from the start — it avoids this entire class of problem. Once your build is working, completing the Kubernetes deployment pipeline with envsubst is the natural next step
- Add a health-check pipeline stage that runs docker info at the start of any build that needs Docker — fail fast with a clear error rather than partway through a build
- Use Jenkins Configuration as Code (JCasC) to keep agent configurations reproducible and reviewable
10. Summary
The Docker permission denied error on Jenkins always comes down to the same root cause: the Jenkins process can't access /var/run/docker.sock. Once your Docker builds are working, you may encounter ECR authentication errors when pushing images to AWS ECR. The fix depends entirely on your deployment setup:
| Setup | Recommended fix |
|---|---|
| Jenkins on bare metal / VM | usermod -aG docker jenkins, then restart the Jenkins service |
| Jenkins in a Docker container | Mount the socket, match the host docker GID inside the container |
| Kubernetes agents (Docker cluster) | DinD sidecar with privileged: true |
| Kubernetes agents (containerd/CRI-O) | Use Kaniko or Buildah — no Docker daemon is available |
If you remember nothing else: usermod -aG docker jenkins works on bare metal, but you must restart Jenkins after. And if you're on Kubernetes, check whether Docker is even available on the node before reaching for DinD.