#  Jenkins in Docker: run Docker inside a Jenkins container

> Set up Jenkins in Docker and enable the Jenkins container to build, run, and push Docker images by bind-mounting the host Docker socket. Step-by-step guide.
> 

**Source:** https://hackmamba.io/engineering/running-docker-in-a-jenkins-container/
**Published:** 5 Apr 2022
**Author:** Divine Odazie
**Category:** Engineering

---
[Jenkins](https://www.jenkins.io/) is an open-source CI/CD tool with a large plugin ecosystem for automating builds, tests, and deployments. Running Jenkins inside a Docker container solves configuration file conflicts that occur when multiple projects share a Jenkins installation on the same host.

The new challenge that creates: if you want to build and push Docker images from within Jenkins, the Jenkins container needs access to Docker. This article covers how to set that up using a custom Jenkins image that bind-mounts the host's Docker socket into the container.

The [source code and Dockerfile for the NodeJS application used in this tutorial](https://github.com/Kikiodazie/Backend-RESTful-API) are on [GitHub](https://github.com/).

## Prerequisites

- Working knowledge of Docker and the terminal
- A [Docker Hub](https://hub.docker.com/) account

## Why not Docker-in-Docker?

The most common suggestion for running Docker inside a container is Docker-in-Docker (DinD) — running a Docker daemon inside the Jenkins container itself. The original DinD approach required the `-privileged` flag, which gives the container full access to the host kernel and introduces security risks including data corruption when multiple containers share `/var/lib/docker`.

Modern alternatives like rootless containers and tools such as [sysbox](https://github.com/nestybox/sysbox) address some of these issues, but for a straightforward CI use case, bind-mounting the host Docker socket is simpler, more stable, and does not require elevated privileges.

## Create a custom Jenkins image

Create a `Dockerfile` with the following content:

```dockerfile
FROM jenkins/jenkins:lts
USER root

RUN apt-get update -qq && \
    apt-get install -y ca-certificates curl && \
    install -m 0755 -d /etc/apt/keyrings && \
    curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc && \
    chmod a+r /etc/apt/keyrings/docker.asc && \
    echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] \
    https://download.docker.com/linux/debian $(. /etc/os-release && echo "$VERSION_CODENAME") stable" \
    > /etc/apt/sources.list.d/docker.list && \
    apt-get update -qq && \
    apt-get install -y docker-ce docker-ce-cli containerd.io

RUN usermod -aG docker jenkins
```

What each section does:

- `FROM jenkins/jenkins:lts` uses the official long-term support Jenkins image as the base
- The `RUN` block installs Docker using the current recommended method from Docker's official documentation — downloading the GPG key into `/etc/apt/keyrings` and adding the Docker apt repository
- `usermod -aG docker jenkins` adds the Jenkins user to the `docker` group so it can run Docker commands without `sudo`

Build the image:

```bash
docker image build -t custom-jenkins-docker .
```

## Run the Jenkins container

Run the custom image with two volume mounts:

- `/var/run/docker.sock:/var/run/docker.sock` — binds the host Docker daemon socket into the container, giving the Jenkins Docker CLI a daemon to talk to
- `jenkins_home:/var/jenkins_home` — persists Jenkins configuration and job data across container restarts

```bash
docker run -it -p 8080:8080 -p 50000:50000 \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v jenkins_home:/var/jenkins_home \
  custom-jenkins-docker
```

Visit `http://localhost:8080` to complete the Jenkins setup.

## Configure Jenkins

The setup wizard asks for an initial admin password. Retrieve it from the container logs printed after the `docker run` command.

![Jenkins getting started page](https://paper-attachments.dropbox.com/s_33CE5684927EB1F665F2EEF2A8A615DFA881F46F04918B588BABDF4D08ACF025_1645484899181_jenkins-getting-started.png)

![Initial admin password in container logs](https://paper-attachments.dropbox.com/s_33CE5684927EB1F665F2EEF2A8A615DFA881F46F04918B588BABDF4D08ACF025_1645553155314_Screenshot+from+2022-02-22+19-05-37.png)

Select **Install suggested plugins** when prompted.

![Jenkins plugin installation screen](https://paper-attachments.dropbox.com/s_33CE5684927EB1F665F2EEF2A8A615DFA881F46F04918B588BABDF4D08ACF025_1645661908679_plugins-installation.png)

Create your admin user and finish the setup wizard.

![Jenkins admin user creation](https://paper-attachments.dropbox.com/s_33CE5684927EB1F665F2EEF2A8A615DFA881F46F04918B588BABDF4D08ACF025_1645717974971_Screenshot+from+2022-02-24+16-52-36.png)

## Dockerize a Node.js app with Jenkins

### Install required plugins

Go to **Manage Jenkins → Manage Plugins → Available** and install:

- **NodeJS Plugin** — enables Node.js build steps
- **CloudBees Docker Build and Publish plugin** — builds Dockerfiles and pushes images to Docker Hub

![Jenkins plugins manager](https://paper-attachments.dropbox.com/s_33CE5684927EB1F665F2EEF2A8A615DFA881F46F04918B588BABDF4D08ACF025_1645718891759_Screenshot+from+2022-02-24+17-07-55.png)

### Configure the NodeJS plugin

Go to **Manage Jenkins → Global Tool Configuration** and add a NodeJS installation.

![NodeJS global tool configuration](https://paper-attachments.dropbox.com/s_33CE5684927EB1F665F2EEF2A8A615DFA881F46F04918B588BABDF4D08ACF025_1645570133589_Screenshot+from+2022-02-22+23-43-28.png)

### Create a build job

On the Jenkins dashboard, click **New Item**, select **Freestyle project**, give it a name, and click **OK**.

![Jenkins new item - freestyle project](https://paper-attachments.dropbox.com/s_33CE5684927EB1F665F2EEF2A8A615DFA881F46F04918B588BABDF4D08ACF025_1645602069526_Screenshot+from+2022-02-23+08-40-27.png)

Under **Source Code Management**, select **Git** and enter the repository URL:
`https://github.com/Kikiodazie/Backend-RESTful-API.git`

Leave **Credentials** blank — the repository is public.

![Jenkins SCM configuration with GitHub URL](https://paper-attachments.dropbox.com/s_33CE5684927EB1F665F2EEF2A8A615DFA881F46F04918B588BABDF4D08ACF025_1645604367689_Screenshot+from+2022-02-23+09-15-47.png)

Under **Build**, add a **Docker Build and Publish** build step.

![Jenkins Docker Build and Publish step](https://paper-attachments.dropbox.com/s_33CE5684927EB1F665F2EEF2A8A615DFA881F46F04918B588BABDF4D08ACF025_1645606022505_Screenshot+from+2022-02-23+09-46-45.png)

Set the **Repository Name** to `your_dockerhub_username/your_repo_name`, for example `kikiodazie/node-docker`.

Leave **Registry credentials** blank for now — authentication is handled in the next step.

![Docker Build and Publish repository name field](https://paper-attachments.dropbox.com/s_33CE5684927EB1F665F2EEF2A8A615DFA881F46F04918B588BABDF4D08ACF025_1645610156349_Screenshot+from+2022-02-23+10-54-13.png)

### Authenticate with Docker Hub inside the container

The Jenkins container needs to be logged into Docker Hub to push images. Get a shell inside the running container:

```bash
docker exec -it <container_name_or_id> /bin/bash
```

Inside the container, log in to Docker Hub:

```bash
docker login
```

Enter your Docker Hub credentials when prompted.

![Docker Hub login inside Jenkins container](https://paper-attachments.dropbox.com/s_33CE5684927EB1F665F2EEF2A8A615DFA881F46F04918B588BABDF4D08ACF025_1645609751364_Screenshot+from+2022-02-23+00-14-27.png)

### Run the build

Back in Jenkins, click **Build Now**. Jenkins will clone the repository, build the Docker image from its Dockerfile, and push it to your Docker Hub account.

View the output in **Build History → Console Output**.

![Jenkins build console output](https://paper-attachments.dropbox.com/s_33CE5684927EB1F665F2EEF2A8A615DFA881F46F04918B588BABDF4D08ACF025_1645611042388_Screenshot+from+2022-02-23+11-10-24.png)

![Successful Docker build and push in Jenkins](https://paper-attachments.dropbox.com/s_33CE5684927EB1F665F2EEF2A8A615DFA881F46F04918B588BABDF4D08ACF025_1645611213322_Screenshot+from+2022-02-23+11-13-15.png)

The image is now built and published to Docker Hub from inside the Jenkins container.

## Where to go next

From here you can add build triggers on GitHub push events using a webhook, parameterize the repository name and Docker Hub credentials as Jenkins environment variables, or extend the pipeline to run tests before the Docker build step. The [Jenkins Docker installation documentation](https://www.jenkins.io/doc/book/installing/docker/) covers additional configuration options including HTTPS and agent setup.

## FAQs

1, **What is the difference between Docker-in-Docker and bind-mounting the Docker socket?**

Docker-in-Docker runs a full Docker daemon inside the container, which historically required the `-privileged` flag and introduced security and data corruption risks. Bind-mounting the host socket (`/var/run/docker.sock`) gives the container's Docker CLI access to the host daemon directly — no privileged mode required, no separate daemon to manage.

2, **Why do I need a custom Jenkins image instead of the official one?**

The official `jenkins/jenkins:lts` image does not include Docker. The custom image adds Docker CE to the Jenkins base image and adds the Jenkins user to the Docker group, which is what enables Jenkins to run Docker commands as part of a build job.

3, **What does `usermod -aG docker jenkins` do?**

It adds the `jenkins` user to the `docker` group. On Linux, the Docker daemon socket is owned by the `docker` group. Without this, the Jenkins user cannot communicate with the socket and all Docker commands in build jobs will fail with a permissions error.

4, **Is bind-mounting the Docker socket secure?**

It gives any process inside the container the same access to the Docker daemon as the host user running the container, which means a compromised build job could affect other containers or the host. For production CI environments, use dedicated Docker-in-Docker solutions with appropriate isolation, or a remote Docker daemon with TLS authentication.

5, **Can I use this setup with a Jenkins pipeline instead of a freestyle project?**

Yes. Once the custom image is running with the socket bind-mount, you can use Docker commands in both freestyle build steps and declarative or scripted Jenkinsfile pipelines. The `docker` CLI is available in the container regardless of how Jenkins invokes it.
