Jenkins Docker Integration for Builds
Docker and Jenkins work together to create consistent, reproducible build environments. Instead of installing Java, Node.js, Python, or any other tool on every build agent, Jenkins pulls a Docker image with those tools pre-installed and runs the build inside a container.
Think of Docker images as pre-packed toolboxes. Instead of buying and organizing every tool yourself, you pick a toolbox that already has exactly what you need, use it for the job, and return it when done. The next person gets the same toolbox in the same condition.
Why Use Docker in Jenkins Pipelines
| Without Docker | With Docker |
|---|---|
| Install Java 17 on every agent manually | Use eclipse-temurin:17 image — no installation |
| Version conflicts between jobs | Each job gets its own isolated container |
| "Works on my machine" problems | Same container runs locally and on Jenkins |
| Cleaning up after a build | Container is destroyed — clean every time |
| Agent must have all tools pre-installed | Agent only needs Docker |
Using Docker as a Pipeline Agent
Run an entire pipeline stage inside a Docker container by specifying the Docker image in the agent block.
pipeline {
agent {
docker {
image 'maven:3.9-eclipse-temurin-17'
args '-v /root/.m2:/root/.m2'
}
}
stages {
stage('Build') {
steps {
sh 'mvn --version' // Uses Maven from the container
sh 'java --version' // Uses Java from the container
sh 'mvn clean package'
}
}
}
}
Jenkins pulls the image, starts a container, runs all the stages inside it, and destroys the container when the pipeline finishes.
Different Docker Images Per Stage
Different stages often need different tools. Use a stage-level agent to run each stage in its own container.
pipeline {
agent none // No global agent — each stage defines its own
stages {
stage('Build Java') {
agent {
docker { image 'maven:3.9-eclipse-temurin-17' }
}
steps {
sh 'mvn clean package -DskipTests'
stash name: 'app-jar', includes: 'target/*.jar'
}
}
stage('Run Tests') {
agent {
docker { image 'maven:3.9-eclipse-temurin-17' }
}
steps {
unstash 'app-jar'
sh 'mvn test'
}
}
stage('Build Frontend') {
agent {
docker { image 'node:20-alpine' }
}
steps {
sh 'npm ci'
sh 'npm run build'
stash name: 'dist', includes: 'dist/**'
}
}
stage('Security Scan') {
agent {
docker { image 'aquasec/trivy:latest' }
}
steps {
unstash 'app-jar'
sh 'trivy fs --exit-code 0 .'
}
}
}
}
Diagram: Each stage runs in its own container
Stage 1: Build Java
[maven:3.9 container starts]
→ mvn clean package
[container destroyed]
Stage 2: Run Tests
[maven:3.9 container starts]
→ mvn test
[container destroyed]
Stage 3: Build Frontend
[node:20 container starts]
→ npm run build
[container destroyed]
Stage 4: Security Scan
[trivy container starts]
→ trivy fs .
[container destroyed]
Each stage gets a fresh, isolated environment.
Building Docker Images Inside Jenkins
Beyond using Docker for build environments, Jenkins also builds and pushes Docker images as part of a delivery pipeline.
Building and Pushing a Docker Image
pipeline {
agent any // Agent must have Docker installed
environment {
REGISTRY = 'registry.mycompany.com'
IMAGE_NAME = 'myapp'
IMAGE_TAG = "${env.BUILD_NUMBER}"
FULL_IMAGE = "${REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG}"
}
stages {
stage('Build App') {
steps {
sh 'mvn clean package -DskipTests'
}
}
stage('Build Docker Image') {
steps {
sh "docker build -t ${FULL_IMAGE} ."
sh "docker tag ${FULL_IMAGE} ${REGISTRY}/${IMAGE_NAME}:latest"
}
}
stage('Push Docker Image') {
steps {
withCredentials([usernamePassword(
credentialsId: 'registry-credentials',
usernameVariable: 'REG_USER',
passwordVariable: 'REG_PASS'
)]) {
sh "docker login ${REGISTRY} -u ${REG_USER} -p ${REG_PASS}"
sh "docker push ${FULL_IMAGE}"
sh "docker push ${REGISTRY}/${IMAGE_NAME}:latest"
}
}
}
stage('Deploy') {
when { branch 'main' }
steps {
sh "kubectl set image deployment/myapp app=${FULL_IMAGE}"
}
}
}
post {
always {
// Remove local image to free disk space
sh "docker rmi ${FULL_IMAGE} || true"
}
}
}
Docker-in-Docker vs Docker Socket Mounting
When your Jenkins agent itself runs inside Docker and you need to build Docker images, you have two approaches.
Option 1: Docker Socket Mounting (recommended)
Mount the host Docker socket into the Jenkins container:
-v /var/run/docker.sock:/var/run/docker.sock
The container uses the host machine's Docker daemon.
Pros: Simple, no extra setup, fast
Cons: Containers built have access to host Docker (security trade-off)
Option 2: Docker-in-Docker (DinD)
Run a full Docker daemon inside the Jenkins container.
Pros: Better isolation
Cons: Complex setup, slower, requires privileged mode
Most teams use Docker socket mounting.
Dockerfile in Your Repository
Your repository should contain a Dockerfile that defines how to build the application image. Jenkins uses this file during the "Build Docker Image" stage.
Example Dockerfile for a Java Spring Boot app: FROM eclipse-temurin:17-jre-alpine WORKDIR /app COPY target/myapp.jar app.jar EXPOSE 8080 ENTRYPOINT ["java", "-jar", "app.jar"]
Repository structure with Dockerfile:
myapp/
├── src/
├── target/
│ └── myapp.jar ← Built by Maven
├── Dockerfile ← Defines the Docker image
├── Jenkinsfile ← Pipeline definition
└── pom.xml
Caching Docker Layers in Jenkins
Docker builds are slow when they download dependencies every time. Cache the Maven or npm dependency folder to speed up builds.
Caching Maven dependencies:
agent {
docker {
image 'maven:3.9-eclipse-temurin-17'
args '-v /root/.m2:/root/.m2'
// ^^^^^^^^^^^^ Mounts Maven cache from host
// Dependencies persist between builds
}
}
Caching npm dependencies:
agent {
docker {
image 'node:20-alpine'
args '-v /var/jenkins_home/npm-cache:/root/.npm'
}
}
Without caching: Build downloads 200 MB of dependencies every run
With caching: Only new or changed dependencies are downloaded
Key Points
- Docker agents in Jenkins provide consistent, isolated build environments without installing tools on agents directly.
- Use stage-level Docker agents to run different stages in different containers.
- Jenkins can build and push Docker images as part of a delivery pipeline using the Docker CLI.
- Mount the Docker socket from the host to enable Docker commands inside a Jenkins container.
- Cache dependency folders (Maven, npm) by mounting volumes to avoid re-downloading packages on every build.
