Jenkins in Docker — Setup & Troubleshooting
December 14, 2025
Running Jenkins in a Docker container and building/pushing Docker images from a pipeline. These are the errors I hit, in order, and the fix for each — for both macOS (Docker Desktop) and Windows (Docker Desktop).
The core idea
The official jenkins/jenkins:lts image has no Docker CLI inside it.
Mounting /var/run/docker.sock only gives access to the host's Docker daemon —
you still need a docker binary inside the container to talk to it. So you must
build your own Jenkins image with the Docker CLI baked in.
Step 1 — Build a Jenkins image with the Docker CLI
Dockerfile (same for Mac and Windows):
FROM jenkins/jenkins:lts
USER root
RUN apt-get update && apt-get install -y \
ca-certificates curl gnupg lsb-release && \
install -m 0755 -d /etc/apt/keyrings && \
curl -fsSL https://download.docker.com/linux/debian/gpg \
| gpg --dearmor -o /etc/apt/keyrings/docker.gpg && \
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/debian $(lsb_release -cs) stable" \
> /etc/apt/sources.list.d/docker.list && \
apt-get update && apt-get install -y docker-ce-cli && \
rm -rf /var/lib/apt/lists/*
USER jenkins
Build it:
docker build -t jenkins:1.0 .
Verify the CLI is actually in the image before running:
docker run --rm jenkins:1.0 which docker
# -> /usr/bin/docker
Step 2 — Run the Jenkins container
Run as root so the container can access the Docker socket. On Docker Desktop
(both OSes) the socket is owned by root inside the VM, so -u root is the
reliable fix — the Linux --group-add docker trick does not work here.
macOS (zsh / Terminal):
docker rm -f jenkins # remove any old container with this name first
docker run -d --name jenkins -p 8080:8080 \
-u root \
-v /var/run/docker.sock:/var/run/docker.sock \
-v jenkins_home:/var/jenkins_home \
jenkins:1.0
Windows (PowerShell) — line continuation is a backtick ` (not \):
docker rm -f jenkins
docker run -d --name jenkins -p 8080:8080 `
-u root `
-v /var/run/docker.sock:/var/run/docker.sock `
-v jenkins_home:/var/jenkins_home `
jenkins:1.0
Windows (CMD) — line continuation is ^:
docker rm -f jenkins
docker run -d --name jenkins -p 8080:8080 ^
-u root ^
-v /var/run/docker.sock:/var/run/docker.sock ^
-v jenkins_home:/var/jenkins_home ^
jenkins:1.0
getentdoes not exist on Mac or Windows — that's a Linux-only command. Don't use--group-add $(getent group docker ...)on Desktop; use-u root.
Error map — symptom → cause → fix
1. docker: not found (exit 127)
- Cause: Docker CLI not installed in the running container (or you're still
running the old
jenkins/jenkins:ltscontainer, not your newjenkins:1.0). - Fix: Rebuild the image (Step 1),
docker rm -f jenkins, re-run (Step 2). Confirm which image is live and that the CLI exists:
docker ps --filter name=jenkins --format '{{.Names}} {{.Image}}'
docker exec jenkins which docker
2. permission denied ... unix:///var/run/docker.sock
- Cause: The
jenkinsuser can't read the Docker socket. - Fix: Recreate the container with
-u root(Step 2).
3. localhost:8080 still shows Jenkins after stopping the container
- Cause: Something else is holding port 8080 — another container, or a native Jenkins installed on the host.
- Fix (find what owns the port):
- Mac:
lsof -i :8080→ ifjava, it's a Homebrew Jenkins →brew services stop jenkins-lts - Windows (PowerShell):
netstat -ano | findstr :8080→ note the PID →tasklist /FI "PID eq <pid>"to identify it → stop the service/process - Also check Docker:
docker ps --filter publish=8080 - If nothing obvious, it's browser cache → hard refresh (
Cmd/Ctrl+Shift+R) or open a private window.
- Mac:
4. fatal: not in a git directory when reading the Jenkinsfile
- Cause: Jenkins "Lightweight checkout" failing.
- Fix: Job → Configure → Pipeline → Definition: Pipeline script from SCM → uncheck "Lightweight checkout" → Save → build.
5. fatal: not in a git directory on the pipeline's Checkout SCM stage
- Cause: Stale/corrupt workspace left in the reused
jenkins_homevolume (ownership changed when switching to-u root). - Fix: Wipe the workspace so Jenkins re-clones fresh:
docker exec jenkins rm -rf /var/jenkins_home/workspace/nccV1
or in the UI: Job → Workspace → Wipe Out Current Workspace. If git 2.47 still complains about "dubious ownership" while running as root:
docker exec jenkins git config --global --add safe.directory '*'
6. invalid reference format on the image tag
- Cause: Docker image names must be lowercase, no
@, in the formatnamespace/repository:tagwhere namespace = your Docker Hub username. The tag was using an email + stray digits. - Fix: Build the tag from the Docker Hub username. If
withCredentialsbindsDOCKERHUB_CREDS, thenDOCKERHUB_CREDS_USRis the username:
sh "docker build -f backend2/Dockerfile -t ${DOCKERHUB_CREDS_USR}/backend:latest ./backend2"
Result: yourusername/backend:latest.
Quick reference
| Task | macOS / Linux | Windows PowerShell |
|---|---|---|
| Remove old container | docker rm -f jenkins | docker rm -f jenkins |
| Build image | docker build -t jenkins:1.0 . | docker build -t jenkins:1.0 . |
| Check CLI in container | docker exec jenkins which docker | docker exec jenkins which docker |
| See what's on port 8080 | lsof -i :8080 | netstat -ano | findstr :8080 |
| Wipe workspace | docker exec jenkins rm -rf /var/jenkins_home/workspace/nccV1 | same |
| Get a shell in container | docker exec -it jenkins bash | docker exec -it jenkins bash |
Sane order to bring it up from scratch
- Write the Dockerfile (Step 1).
docker build -t jenkins:1.0 .docker run --rm jenkins:1.0 which docker— confirm the CLI is present.docker rm -f jenkins(clear old container/name).- Run with
-u root+ socket mount (Step 2). - Make sure nothing else holds 8080 (error #3 checks).
- In the job: uncheck "Lightweight checkout".
- Fix the image tag in the Jenkinsfile to
username/repo:latest. - Build. Check the
docker logout/login and push stage credentials.
Next: [[jenkins-docker-hub-credentials]], [[jenkins-pipeline-job]], and [[jenkins-jenkinsfile-explained]].