How to Resolve ‘docker: not found’ in Jenkins Docker Container Pipelines

Marianne elanotta

Marianne is a graduate in communication technologies and enjoys sharing the latest technological advances across various fields. Her programming skills include Java OO and Javascript, and she prefers working on open-source operating systems. In her free time, she enjoys playing chess and computer games with her two children.

If you’re running Jenkins inside a Docker container for your Continuous Integration and Continuous Delivery (CI/CD) pipelines,

you’ve probably bumped into a common head-scratcher when trying to build other Docker images from your Jenkins jobs. Sometimes, your pipelines just stop dead in their tracks, throwing an error that looks something like `docker: not found`.

What does that even mean? Essentially, the environment where your Jenkins job is running – that specific container – can’t seem to find the `docker` command-line tool. This usually happens when you’ve started your Jenkins container with the Docker socket mounted (like using the `-v /var/run/docker.sock:/var/run/docker.sock` option) to let it talk to the Docker engine on your host machine, but you haven’t actually put the necessary Docker client program *inside* the Jenkins container itself.

In this post, I’ll walk you through why this error pops up and share a few solid ways you can get Docker commands up and running within your containerized Jenkins setup. Let’s get this fixed so your builds can sail smoothly!

Why This Error Occurs: The Missing Docker Client Program

The root cause of this `docker: not found` error is pretty straightforward: the standard Jenkins Docker images (like the ones based on `jenkins/jenkins:lts`) don’t come with the Docker CLI bundled in. Mounting the host’s Docker socket (`/var/run/docker.sock`) is like giving your Jenkins container a phone line to the Docker daemon (the background service running Docker operations) on your host.

But, to actually *use* that phone line and tell the daemon what to do (like “build this image!” or “list running containers!”), you need the Docker client program – the executable that understands commands like `docker build` or `docker ps`. If this client isn’t installed inside your Jenkins container, any attempt to run a command starting with `docker` will fail because the container’s operating system can’t find that executable anywhere in its list of known command locations (its `PATH`).

So, you really need two things for Jenkins in Docker to build Docker images:

  1. Access to the Docker Engine: This is usually achieved by mounting the `/var/run/docker.sock` file from the host into the Jenkins container. This allows communication.
  2. The Docker Client Program (CLI): This executable needs to be present and accessible *within* the Jenkins container’s file system so you can actually type and run `docker` commands.

The `docker: not found` error is a clear sign that the second part – the client program – is missing.

Practical Solutions for Running Docker Commands in Jenkins

Alright, let’s look at how to tackle this. There are several ways to get the Docker client into your Jenkins container environment, ranging from methods that are quick but less permanent, to more robust, production-ready approaches.

1. Build a Custom Jenkins Image with the Docker Client

This is often the most recommended approach, especially for consistent and repeatable builds. You create your own Docker image based on the official Jenkins image, but you add the Docker CLI tools right into it. This way, every time you start a container from your custom image, Docker is already there and ready.

Here’s what a simple `Dockerfile` for this might look like:


FROM jenkins/jenkins:lts-jdk11 # Start from a specific Jenkins LTS version (good practice!)

USER root # Switch to root to install software

# Install Docker CLI - Method A: Recommended using the official script
# This script detects your system and installs the latest stable version
RUN curl -fsSL https://get.docker.com/get-docker.sh | sh

# Install Docker CLI - Method B: Downloading specific static binaries (more control over version)
# Uncomment and adjust version/arch as needed if Method A isn't suitable or you need a specific older 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 the 'jenkins' user to the 'docker' group.
# This group is created by the Docker installation script and is necessary
# for the 'jenkins' user to interact with the Docker socket (/var/run/docker.sock)
# without needing root privileges inside the container.
RUN usermod -aG docker jenkins

USER jenkins # Switch back to the 'jenkins' user for security

Once you have this `Dockerfile`, build it using a command like `docker build -t my-custom-jenkins -f ./path/to/YourDockerfileJenkinsName .`. Then, when you run your Jenkins container, use this new image. Remember, you still need to mount the Docker socket so Jenkins can talk to the host’s Docker daemon:


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 ensures the Docker client is installed correctly *inside* the image, and adding the `jenkins` user to the `docker` group handles the necessary permissions to talk to the socket.

If you’re using Docker Compose, you can manage this build and run process elegantly in a `docker-compose.yml` file:


version: '3.8' # Use a recent Compose version
services:
  jenkins:
    # Configure Docker Compose to build your custom image
    build:
      context: . # Build context (usually the directory containing the docker-compose.yml and Dockerfile)
      dockerfile: YourDockerfileJenkinsName # Point to your custom Dockerfile
    image: my-custom-jenkins # Optional: assign a name to the resulting image
    container_name: jenkins_service # A friendly name for the container
    restart: always # Ensure Jenkins restarts if it crashes or Docker restarts
    ports:
      - "8080:8080" # Standard Jenkins web UI port
      - "50000:50000" # Agent discovery port
    volumes:
      - jenkins_data:/var/jenkins_home # Use a named volume to persist Jenkins data
      - /var/run/docker.sock:/var/run/docker.sock # Mount the host's Docker socket!

    # Important Note: Only consider these if usermod -aG docker jenkins isn't enough,
    # but try to avoid them due to security implications.
    # user: "0:0" # Run as root user (UID 0) and root group (GID 0) - Less secure!
    # privileged: true # Grant almost all capabilities to the container - Highly insecure!

# Define the named volume
volumes:
  jenkins_data:

Using `USER root` temporarily in the `Dockerfile` just for installing packages and then switching back to `USER jenkins` is a standard secure pattern. The key permissions trick here is adding the `jenkins` user to the `docker` group, which needs to line up correctly with the group ownership and permissions of the `/var/run/docker.sock` file on your host machine.

2. Use Jenkins Global Tool Configuration

Jenkins has a neat feature that lets it download and manage tools automatically, including the Docker client. This can be handy if you don’t want to build a whole new Docker image just for the client, although it requires some setup within the Jenkins UI and your Jenkinsfile.

Here’s how you’d typically set it up:

  1. Log into Jenkins and go to “Manage Jenkins”.
  2. Select “Global Tool Configuration”.
  3. Scroll down until you see the “Docker” section (or “Docker Installations”) and click the “Add Docker” button.
  4. Give your Docker installation a memorable “Name” (e.g., `docker-latest-download`).
  5. Check the box labeled “Install automatically”.
  6. Click “Add installer” and choose the “Download from docker.com” option. You can usually leave the version as “latest” or pick a specific one.
  7. Hit “Save” at the bottom of the page.

Now, in your Jenkins Pipeline script (`Jenkinsfile`), you need to tell Jenkins to use this configured tool and make its binaries available in the execution environment’s `PATH`:


pipeline {
    agent any 
    stages {
        stage('Setup Environment') {
            steps {
                script {


                    def dockerToolInfo = tool name: 'docker-latest-download', type: 'dockerTool'

                    env.PATH = "${dockerToolInfo.getHome()}/bin:${env.PATH}"
                    sh 'echo "PATH is now: $PATH"' 
                }
            }
        }
        stage('Build Application Docker Image') {
            steps {
                sh 'docker --version' 
                sh 'docker build -t my-app .' 

            }
        }
    }
}

A common issue after doing this is still facing permissions errors when the `docker` command tries to talk to `/var/run/docker.sock` (like “Cannot connect to the Docker daemon”). This means the `jenkins` user inside the container doesn’t have permission to access the socket file.

You’ll likely need to address this by ensuring the Jenkins user is in a group that has access, often involving similar steps to adding the user to the `docker` group as shown in the Custom Image solution, possibly by using `docker exec` manually after the container starts or by adjusting group IDs.

3. Directly Mount the Host’s Docker CLI (Use with Caution!)

Another, though generally less recommended, approach is to directly share the Docker client executable file from your host machine into the Jenkins container using a volume mount. This skips installing it but introduces potential compatibility problems.

You’d add an extra 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 the host's docker executable
  jenkins/jenkins:lts # Use a standard Jenkins image

Or, in your `docker-compose.yml` file:


# ... other parts of your docker-compose.yml
services:
  jenkins:
    image: jenkins/jenkins:lts # Use a standard image
    # ... other configurations
    volumes:
      - your_jenkins_home_volume_or_path:/var/jenkins_home
      - /var/run/docker.sock:/var/run/docker.sock
      - /usr/bin/docker:/usr/bin/docker # Mount the host's docker binary.
                                         # Note: You might need to adjust /usr/bin/docker if your host's binary is elsewhere.
                                         # Using $(which docker) directly in docker-compose might not work as expected.
# ... rest of the file

Important Caution: This method of mounting the host’s Docker executable directly into the container is generally discouraged for production use. The Docker client binary often relies on specific libraries present on the host system. If the Jenkins container’s environment doesn’t have compatible versions (or the libraries at all), the Docker client binary you mounted might not run correctly or could behave unexpectedly, leading to hard-to-debug errors. Installing a compatible Docker client *within* the container (Solution 1 or 2) is almost always more reliable and stable.

4. Use a Pre-configured Jenkins Image with Docker

To make things even simpler, some community-maintained Docker images are available that already bundle Jenkins and the Docker CLI together. An example image sometimes mentioned is `getintodevops/jenkins-withdocker:lts` (always verify the source and maintenance status of third-party images!).

Using such an image means your `docker run` command is quite straightforward:


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 # Use the pre-built image

While convenient, remember to do your homework on any third-party image. Check where it comes from, how frequently it’s updated, and what exactly is included to ensure it meets your security and stability requirements.

Handling Docker Socket Permissions

Once you’ve successfully made the Docker CLI available inside your Jenkins container, the next potential hurdle is permissions. The user running the Jenkins process inside the container needs permission to read and write to the mounted Docker socket file (`/var/run/docker.sock`). On most Linux hosts, this socket file is owned by `root` and the `docker` group, with read/write permissions for the group.

Here are typical ways to address this:

  • Add the `jenkins` user to the `docker` group (in your custom image): As demonstrated in Solution 1, the command `usermod -aG docker jenkins` in your `Dockerfile` is the clean way to do this permanently. This assumes that the `docker` group exists inside the container *and* has a Group ID (GID) that matches the GID of the `docker` group that owns the `/var/run/docker.sock` file on your host. If the GIDs don’t match, this might not work directly, or you might need more advanced Linux permissions handling or bind mounts.
  • Use the `–group-add` flag when running the container: Docker’s `run` command has a `–group-add` option that lets you add the container’s main user to additional groups by GID. You can try adding the Jenkins user to the GID of the host’s `docker` group.
    
    docker run --group-add $(getent group docker | cut -d: -f3) ... # Gets the GID of the host's docker group
    

    Alternatively, just try `–group-add docker` if Docker can correctly map the group name, though relying on GID is often more explicit:

    
    docker run --group-add docker ...
    

    This approach can sometimes be finicky if the group name or GID isn’t found or doesn’t map correctly.

  • Run the Jenkins Container as Root (Not Recommended!): You could force the container to run as the `root` user (e.g., using `user: root` in docker-compose or `-u root` in `docker run`). Since `root` usually has access to everything, this often solves the permission issue. However, this is a **major security risk** as it grants the Jenkins process inside the container root access to the Docker daemon (and potentially the host file system via volumes). Avoid this unless you fully understand the implications and have no other alternative.

The most secure and reliable permission strategy is typically baked into a custom image: ensuring the `jenkins` user is part of a group with a GID that matches the host’s Docker group GID, and that this group has the necessary read/write access to `/var/run/docker.sock`.

Verification Steps

After implementing one of the solutions, the best way to confirm it worked is to simply run a test Jenkins pipeline job that attempts to execute a basic Docker command. Create a new job and use a simple Pipeline script like this:


pipeline {
    agent any 
    stages {
        stage('Verify Docker Connectivity') {
            steps {
                sh 'docker --version' 
                sh 'docker ps -a'     
            }
        }
    }
}

Run this job. If the console output shows the Docker version and a list of containers (even an empty one), congratulations! You’ve fixed the `docker: not found` error and established communication with the Docker daemon. If it still fails with the same error, double-check that the Docker CLI was installed correctly and is in the container’s `PATH`. If it fails with a “permission denied” or “cannot connect” error, revisit the permissions section.

Key Considerations Summary

To wrap things up and ensure a smooth Jenkins-Docker integration, always keep these points in mind:

  • Make absolutely sure the Docker socket file (`/var/run/docker.sock`) from your host is correctly mounted into your Jenkins container. This is non-negotiable for the container to talk to the daemon.
  • Verify that the user running the Jenkins process *inside* the container has the necessary permissions to read from and write to that mounted Docker socket. Permissions are a frequent source of follow-up issues.
  • Pin your image versions! Using tags like `:lts` or `:lts-jdk11` is much better than `:latest` to prevent unexpected changes breaking your setup.
  • Stick to the principle of least privilege. Avoid running the Jenkins container as the `root` user or with the `–privileged` flag unless there is absolutely no other way and you accept the significant security risks involved.

Conclusion

Hitting the “docker: not found” error when you’re trying to run Docker commands from within a Jenkins container is a super common issue, but it boils down to the Docker client executable simply not being present where the Jenkins job is looking for it. By understanding that you need both socket access *and* the client program, you can choose the best solution for your needs.

Whether you decide to build a custom Jenkins image with the Docker client pre-installed (often the most recommended approach for stability and control), leverage Jenkins’ built-in tool management, or explore other methods, and crucially, by getting the Docker socket permissions right, you can successfully integrate Docker build and management capabilities directly into your Jenkins CI/CD pipelines. Building a dedicated image tailored to your needs is usually the most robust and maintainable path forward.

 

 


If you like the content, we would appreciate your support by buying us a coffee. Thank you so much for your visit and support.

 

Leave a Reply