Introduction
Recently, I embarked on setting up a CI/CD pipeline for my Jira clone project. While I was familiar with Azure DevOps and Azure Pipelines, I wanted to explore something new, so I opted for Jenkins running in a Docker container. This post details my journey, including the setup process, challenges I encountered, and how I resolved them.
Project Context
Before diving into the Jenkins setup, here's some context about my project:
- Built a Jira clone with modern web technologies
- Infrastructure provisioned with Terraform
- Wanted a robust CI/CD pipeline for automated testing and deployment
- Needed to work with Docker images as part of the pipeline
Setting Up Jenkins with Docker
1. Running Jenkins in a Docker Container
The first step was to run Jenkins in a Docker container with access to the Docker daemon:
docker run -p 8080:8080 -p 50000:50000 \
-v jenkins_home:/var/jenkins_home \
-v /var/run/docker.sock:/var/run/docker.sock \
jenkins/jenkins:lts
This command:
- Exposes Jenkins on port 8080
- Maps the Jenkins agent port (50000)
- Creates a persistent volume for Jenkins data
- Mounts the Docker socket to allow Docker commands within Jenkins
2. Fixing Docker Socket Permissions
One of the first issues I encountered was permission problems with the Docker socket. To fix this, I ran:
sudo chmod 666 /var/run/docker.sock
3. Retrieving the Jenkins Admin Password
To complete the Jenkins setup, I needed the initial admin password:
docker exec ${CONTAINER_ID} cat /var/jenkins_home/secrets/initialAdminPassword
4. Installing Docker CLI Inside the Jenkins Container
For Docker commands to work inside the Jenkins container, I needed to install the Docker CLI:
# Log in as root
docker exec -it -u root ${CONTAINER_ID} bash
# Install Docker CLI
apt-get update
apt-get install -y apt-transport-https ca-certificates curl gnupg lsb-release
curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
apt-get update
apt-get install -y docker-ce-cli
# Add Jenkins user to the docker group
groupadd -f docker
usermod -aG docker jenkins
# Exit container
exit
5. Restarting the Jenkins Container
After making these changes, I restarted the container:
docker restart ${CONTAINER_ID}
Jenkins Configuration
With the container set up, I moved on to configuring Jenkins itself:
6.1 Installing Required Plugins
I installed the following plugins:
- NodeJS Plugin (for my JavaScript-based project)
- Docker Pipeline Plugin (for Docker integration in pipelines)
6.2 Configuring NodeJS
This is where I encountered a significant challenge. Even after installing the NodeJS plugin, I needed to explicitly configure it in the Tools section:
- Go to "Manage Jenkins" > "Tools"
- Enable NodeJS
- Add a NodeJS installation with version 18.17 or higher
- Save the configuration
Important: I discovered that it's not enough to just install the NodeJS plugin. You must also select the NodeJS version in the Tools section, and then explicitly reference this NodeJS installation in your Jenkinsfile with the nodejs
wrapper.
6.3 Setting Up Credentials
For my project, I needed several credentials:
- Go to "Manage Jenkins" > "Manage Credentials"
- Add the following as "Secret text":
-
DATABASE_URL_CREDENTIAL
: PostgreSQL Neon database URL -
CLERK_SECRET_KEY_CREDENTIAL
: Clerk secret key -
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY_CREDENTIAL
: Clerk publishable key -
DOCKER_CREDENTIALS
: Docker Hub credentials (Username with password)
-
Challenges Faced and Solutions
1. Docker Socket Permission Issues in WSL
Since I was using WSL (Windows Subsystem for Linux), I encountered occasional issues with Docker socket permissions. This happened because permissions sometimes reset after system reboots.
Solution: I created a small script to check and fix permissions at startup:
#!/bin/bash
if [ "$(stat -c %a /var/run/docker.sock)" != "666" ]; then
sudo chmod 666 /var/run/docker.sock
echo "Fixed Docker socket permissions"
fi
2. Docker Network Issues with "Docker-in-Docker"
I faced networking challenges when trying to connect containers created by Jenkins to other containers.
Solution: I adopted a "Docker-outside-of-Docker" approach by mounting the host's Docker socket rather than running Docker inside Docker. This way, all containers are created on the host's Docker network.
3. NodeJS Configuration Issues
As mentioned earlier, I discovered that installing the NodeJS plugin wasn't enough. The build would fail with errors about missing Node or npm commands.
Solution:
- Explicitly configure NodeJS in the Tools section
- Reference this specific NodeJS installation in the Jenkinsfile:
pipeline {
agent any
tools {
nodejs 'NodeJS 18.17.0' // Must match the name given in Tools configuration
}
stages {
stage('Build') {
steps {
sh 'node --version'
sh 'npm --version'
sh 'npm install'
// Rest of build steps
}
}
// Other stages
}
}
My Jenkinsfile
Here's a simplified version of the Jenkinsfile I'm using for my Jira clone project:
pipeline {
agent any
tools {
nodejs 'NodeJS 18.17.0'
}
environment {
DOCKER_REGISTRY = 'docker.io'
DOCKER_IMAGE_NAME = 'my-jira-clone'
DOCKER_IMAGE_TAG = "${env.BUILD_NUMBER}"
DATABASE_URL = credentials('DATABASE_URL_CREDENTIAL')
CLERK_SECRET_KEY = credentials('CLERK_SECRET_KEY_CREDENTIAL')
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY = credentials('NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY_CREDENTIAL')
}
stages {
stage('Checkout') {
steps {
checkout scm
}
}
stage('Install Dependencies') {
steps {
sh 'npm install'
}
}
stage('Run Tests') {
steps {
sh 'npm test'
}
}
stage('Build Docker Image') {
steps {
sh "docker build -t ${DOCKER_REGISTRY}/${DOCKER_IMAGE_NAME}:${DOCKER_IMAGE_TAG} ."
}
}
stage('Push Docker Image') {
steps {
withCredentials([usernamePassword(credentialsId: 'DOCKER_CREDENTIALS', usernameVariable: 'DOCKER_USERNAME', passwordVariable: 'DOCKER_PASSWORD')]) {
sh "echo ${DOCKER_PASSWORD} | docker login ${DOCKER_REGISTRY} -u ${DOCKER_USERNAME} --password-stdin"
sh "docker push ${DOCKER_REGISTRY}/${DOCKER_IMAGE_NAME}:${DOCKER_IMAGE_TAG}"
}
}
}
stage('Deploy') {
steps {
// Deployment steps - in my case, updating Terraform or triggering a deployment script
echo "Deploying version ${DOCKER_IMAGE_TAG}"
// Additional deployment commands
}
}
}
post {
always {
sh 'docker logout ${DOCKER_REGISTRY}'
cleanWs()
}
}
}
Conclusion
Setting up Jenkins with Docker for my Jira clone project was an educational journey. While I encountered several challenges, particularly around Docker permissions, networking, and NodeJS configuration, the solutions I found have resulted in a stable and efficient CI/CD pipeline.
The key takeaways from my experience:
- Always explicitly configure tools in Jenkins, even after installing plugins
- Be aware of permission issues when mounting the Docker socket
- Consider the networking implications when working with Docker in CI/CD
- Document your setup process (as I'm doing here!) to make future maintenance easier
If you're considering Jenkins for your CI/CD needs, especially in a Docker environment, I hope my experience helps you avoid some of the pitfalls I encountered.
Have you set up Jenkins for your projects? What challenges did you face? I'd love to hear about your experiences in the comments!
This post is part of my series on building and deploying a Jira clone application. Check out my profile for more posts in this series.
Top comments (4)
** My Experience: When Reality Hits**
Jenkins in Docker
Jenkins in Docker sounds simple. But once you need Jenkins to:
Mount volumes for persistent job/workspace data
Communicate with Docker outside the container
Build Docker images from inside Jenkins
…you hit classic Docker-in-Docker (DinD) or permission issues. Often, you’ll need to:
Add the container to the host’s Docker group
Mount the Docker socket (-v /var/run/docker.sock:/var/run/docker.sock)
Change jenkins user permissions
Adjust SELinux or AppArmor settings (on some distros)
Minikube Using Docker Driver
I’ve used minikube start --driver=docker and while it's lighter than VirtualBox or KVM, I’ve still run into:
DNS issues inside pods
Persistent volume problems
Resource allocation constraints (especially RAM/CPU)
Port forwarding limitations if not configured carefully
These hiccups might not show up in tutorials but definitely surface in real use.
Grafana in Docker
Grafana is relatively simple in a container, but complications arise when you:
Mount dashboards and data sources from the host
Try to connect to services on the host machine (e.g., Prometheus or PostgreSQL)
Persist data using volumes or external databases
Sometimes Grafana will start clean but fail to read a config file or lose data after a container restart — issues caused by volume misconfigurations or file permission mismatches.
hey thanks for your valuable input in this i just focused on cicd more than docker ofcourese will face issues with this approach i hope in future i will deploy the solution which wont cause any complications
I totally get where you're coming from — when you're focused on CI/CD, it's easy to overlook how Docker’s behavior or any application behavior can throw unexpected wrenches into the workflow. I learned the hard way that containerizing tools like Jenkins, Minikube, or Grafana or a slew of other apps often introduces their own set of challenges, especially around permissions, volume mounts, and network access. Every time I think I've overcome or conquered an issue, I find that I'm at the base of yet another challenge or problem.. But all in all, that is the beauty of it all.
sounds great!!!!