When utilizing Jenkins within a Docker container for Continuous Integration and Continuous Delivery (CI/CD) workflows, a frequent challenge arises during attempts to build Docker images.
Pipelines may abruptly halt with an error message similar to docker: not found
. This indicates that the Jenkins environment, specifically the container it’s running in, cannot locate the Docker command-line interface (CLI).
Typically, this issue surfaces when Jenkins is started with the Docker socket mounted (e.g., -v /var/run/docker.sock:/var/run/docker.sock
), intending to use the host’s Docker daemon, but without the necessary client tools inside the Jenkins container itself. This article explores the underlying reasons for this problem and presents several practical solutions to enable Docker operations within your containerized Jenkins pipelines.
Why This Error Occurs: The Missing Docker Client
The core of the problem is straightforward: the standard Jenkins Docker images (like jenkins/jenkins:lts
) do not bundle the Docker CLI. While mounting the host’s Docker socket (/var/run/docker.sock
) allows the Jenkins container to communicate with the Docker daemon running on the host machine, this communication requires a Docker client program.
If this client is not installed within the Jenkins container, any command starting with docker
(e.g., docker build
, docker ps
) will fail because the executable cannot be found in the container’s PATH
.
Effectively, two components are necessary:
- Access to a Docker Daemon: Usually achieved by volume-mounting the host’s Docker socket.
- Docker Client (CLI): Must be installed and accessible within the Jenkins container.
The docker: not found
error signals that the second component is missing.
Read: How to Fix the Docker Error: ‘bind: address already in use’;
Strategies for Enabling Docker Commands in Jenkins
Several approaches can resolve this issue, ranging from installing the Docker client directly into a running container (less ideal for persistence) to building custom Jenkins images or using Jenkins’ built-in tool management.
1. Installing the Docker Client within a Custom Jenkins Image
A robust and recommended method involves creating a custom Docker image based on an official Jenkins image, but with the Docker CLI tools added. This ensures the tools are always available when the container starts.
You can create a Dockerfile
like the following:
Read: Setting Timezones in Docker Containers
Using code:
FROM jenkins/jenkins:lts-jdk11 # Always pin to a specific LTS version
USER root
# Install Docker CLI
# Method A: Using the official get.docker.com script
RUN curl -fsSL https://get.docker.com/get-docker.sh | sh
# Method B: Downloading specific binaries (example for a specific version)
# RUN curl -fsSLO https://download.docker.com/linux/static/stable/x86_64/docker-17.04.0-ce.tgz \
# && tar xzvf docker-17.04.0-ce.tgz \
# && mv docker/docker /usr/local/bin \
# && rm -r docker docker-17.04.0-ce.tgz
# Add jenkins user to the docker group (created by the Docker installation script)
# This grants permission to use the Docker socket
RUN usermod -aG docker jenkins
USER jenkins
Read: How to Fix Docker Error: Executable File Not Found in $PATH
Build this Dockerfile (e.g., docker build -t my-custom-jenkins -f #YourDockerfileJenkinsName# .
) and then run your Jenkins container using this new image. Remember to still mount the Docker socket:
docker run --name myjenkins -p 8080:8080 -p 50000:50000 \
-v #your_jenkins_home_volume_or_path#:/var/jenkins_home \
-v /var/run/docker.sock:/var/run/docker.sock \
my-custom-jenkins
This approach ensures that the Docker client is correctly installed and the jenkins
user has appropriate permissions.
Alternatively, a docker-compose.yml
file can manage the build and deployment of this custom Jenkins service. For instance:
Using code:
version: '3.8'
services:
jenkins:
build:
context: .
dockerfile: #YourDockerfileJenkinsName# # Points to your custom Dockerfile
image: my-custom-jenkins # Optional: names the built image
container_name: jenkins_service
restart: always
ports:
- "8080:8080"
- "50000:50000"
volumes:
- jenkins_data:/var/jenkins_home # Use a named volume for jenkins_home
- /var/run/docker.sock:/var/run/docker.sock
# Consider not running as root or privileged if usermod -aG docker jenkins is effective
# user: root # if jenkins user needs root privileges temporarily during setup
# privileged: true # Use with caution, only if absolutely necessary
volumes:
jenkins_data:
Using USER root
temporarily in the Dockerfile for installation and then switching back to USER jenkins
is a common pattern. Adding the `jenkins` user to the `docker` group (which should own `docker.sock` or have access to it) is crucial for permission handling.
Read: How to Fix Docker Permission Denied Errors When Connecting to docker.sock
2. Leveraging Jenkins Global Tool Configuration
Jenkins can automatically download and install tools, including Docker, through its administrative interface. This method avoids building a custom Docker image but requires configuration within Jenkins itself.
- Navigate to “Manage Jenkins” → “Global Tool Configuration”.
- Scroll down to “Docker” (or “Docker Installations”) and click “Add Docker”.
- Provide a “Name” (e.g.,
docker-latest
). - Check the box for “Install automatically”.
- Click “Add installer” and choose “Download from docker.com”. Select a version or leave as “latest”.
- Save the configuration.
Then, in your Jenkinsfile, you must reference this tool and add its binaries to the PATH
environment variable for your pipeline stages:
pipeline {
agent any
stages {
stage('Initialize Docker') {
steps {
script {
// 'docker-latest' must match the name configured in Global Tool Configuration
def dockerHome = tool name: 'docker-latest', type: 'dockerTool'
env.PATH = "${dockerHome}/bin:${env.PATH}"
}
}
}
stage('Build Docker Image') {
steps {
sh 'docker --version'
sh 'docker build -t my-app .'
// other docker commands
}
}
}
}
A potential follow-up issue here can be permissions to /var/run/docker.sock
. If you encounter “Cannot connect to the Docker daemon” errors, ensure the Jenkins user (inside the container) has permissions. This often involves adding the jenkins
user to the docker
group (see solution 1’s Dockerfile or manually via docker exec
if the group GID matches appropriately).
3. Mounting the Host’s Docker CLI (Use with Caution)
Another method involves directly mounting the host’s Docker client binary into the Jenkins container. This can be achieved by adding a volume mount to your docker run
command:
docker run --name myjenkins -p 8080:8080 -p 50000:50000 \
-v #your_jenkins_home_volume_or_path#:/var/jenkins_home \
-v /var/run/docker.sock:/var/run/docker.sock \
-v $(which docker):/usr/bin/docker \ # Mounts host docker CLI
jenkins/jenkins:lts
Or, in a docker-compose.yml
file:
# ...
volumes:
- #your_jenkins_home_volume_or_path#:/var/jenkins_home
- /var/run/docker.sock:/var/run/docker.sock
- /usr/bin/docker:/usr/bin/docker # Assuming host docker is at /usr/bin/docker
# or use a path resolved by $(which docker) if compose preprocesses it
# ...
Important Consideration: This technique of mounting the host’s Docker binary is generally discouraged. The Docker client might be dynamically linked against libraries on the host system that are not present or are incompatible within the Jenkins container’s environment. This can lead to unexpected errors or failures.
It is often more reliable to install a compatible Docker client directly within the Jenkins container or image.
4. Utilizing Pre-configured Jenkins Images with Docker
Some third-party Docker images come with Jenkins and Docker CLI pre-installed and configured. An example mentioned is getintodevops/jenkins-withdocker:lts
.
Using such an image can simplify setup:
docker run --name myjenkins -p 8080:8080 -p 50000:50000 \
-v #your_jenkins_home_volume_or_path#:/var/jenkins_home \
-v /var/run/docker.sock:/var/run/docker.sock \
getintodevops/jenkins-withdocker:lts
When opting for this, ensure the image is from a trusted source and is well-maintained.
Managing Docker Socket Permissions
Regardless of how the Docker CLI is made available, the Jenkins user inside the container must have permission to access the mounted Docker socket (/var/run/docker.sock
). If the socket on the host is owned by root:docker
with group-write permissions, common solutions include:
- Adding Jenkins to Docker Group (in Image): As shown in Solution 1, use
usermod -aG docker jenkins
in your custom Dockerfile. This requires thedocker
group GID inside the container to match the GID of thedocker.sock
file on the host, or for the socket to be more permissively configured (less secure). - Using
--group-add
: When running the container, you can try adding the Jenkins user to the host’sdocker
group by GID.docker run --group-add $(getent group docker | cut -d: -f3) ...
Or, if the group name
docker
is expected to map correctly:docker run --group-add docker ...
This approach can fail if the
docker
group doesn’t exist on the host or cannot be found by the Docker daemon. - Running Jenkins Container as Root (Not Recommended): Setting
user: root
indocker-compose.yml
or-u root
indocker run
will grant permissions but is a significant security risk.
The most secure and reliable permission setup usually involves ensuring the jenkins
user inside the container is part of a group that has the same GID as the group owning the docker.sock
on the host, and that this group has appropriate access rights to the socket.
Verification Steps
Once a solution is implemented, verify its effectiveness by running a simple Jenkins pipeline that executes a Docker command. For example:
pipeline {
agent any
stages {
stage('Verify Docker Access') {
steps {
sh 'docker --version'
sh 'docker ps -a'
}
}
}
}
A successful execution of these commands in the Jenkins job console output, without the docker: not found
error, confirms that the Docker CLI is accessible and functional.
Key Considerations Summary
Additionally, always ensure:
- The Docker socket (
/var/run/docker.sock
) is correctly mounted into the Jenkins container. - The Jenkins user within the container has the necessary permissions to access the Docker socket.
- Pin image versions (e.g.,
jenkins/jenkins:lts-jdk17
) for consistency and to avoid unexpected changes from:latest
tags. - Strive for the principle of least privilege, avoiding running containers as
root
or with--privileged
unless absolutely necessary and fully understood.
Conclusion
The “docker: not found” error in a containerized Jenkins setup is a common hurdle, primarily stemming from the absence of the Docker CLI within the Jenkins container. By implementing one of the discussed solutions, such as building a custom Jenkins image with the Docker client, utilizing Jenkins’ Global Tool Configuration, or carefully considering other methods, and ensuring correct permissions for the Docker socket, it is possible to seamlessly integrate Docker build and management capabilities into Jenkins pipelines.
The most robust and maintainable approach typically involves creating a dedicated Jenkins image tailored to your CI/CD needs, including the necessary version of the Docker client.