What You'll Learn
A complete Jenkins pipeline guide — from declarative pipeline syntax to agents, stages, credentials, shared libraries, and real-world CI/CD implementation with Docker and Kubernetes.
What is a Jenkins Pipeline?
A Jenkins Pipeline is a suite of plugins that supports implementing and integrating Continuous Delivery pipelines into Jenkins. It provides a scriptable, code-based approach to defining your build, test, and deploy process — stored as a Jenkinsfile in your repository.
Declarative Pipeline
- ✅ Structured, opinionated syntax
- ✅ Easier to read and validate
- ✅ Built-in features (post, options)
- ✅ Best for most CI/CD use cases
- ✅ Recommended for teams
Scripted Pipeline
- ⚙️ Full Groovy DSL flexibility
- ⚙️ Complex conditional logic
- ⚙️ More verbose
- ⚙️ Harder to learn
- ⚙️ Use for advanced use cases only
Complete Declarative Jenkinsfile
pipeline {
// Where to run (any available agent)
agent any
// Environment variables available to all stages
environment {
APP_NAME = 'my-app'
DOCKER_REGISTRY = 'registry.example.com'
IMAGE_TAG = "${BUILD_NUMBER}-${GIT_COMMIT[0..7]}"
DOCKER_CREDS = credentials('docker-registry-credentials')
KUBECONFIG = credentials('k8s-kubeconfig')
}
// Pipeline-wide options
options {
buildDiscarder(logRotator(numToKeepStr: '10'))
timeout(time: 30, unit: 'MINUTES')
disableConcurrentBuilds()
timestamps()
}
// Run pipeline on every push
triggers {
pollSCM('H/5 * * * *') // Poll SCM every 5 minutes
}
stages {
// ─────────────── Stage 1: Code Quality ───────────────
stage('Code Quality') {
parallel {
stage('Lint') {
steps {
sh 'npm run lint'
}
}
stage('Security Scan') {
steps {
sh 'npm audit --audit-level=high'
}
}
}
}
// ─────────────── Stage 2: Tests ───────────────
stage('Test') {
steps {
sh '''
npm ci
npm test -- --coverage --ci
'''
}
post {
always {
// Publish test results
junit 'test-results/*.xml'
// Publish coverage
publishHTML([
allowMissing: false,
reportDir: 'coverage',
reportFiles: 'index.html',
reportName: 'Coverage Report'
])
}
}
}
// ─────────────── Stage 3: Build Docker Image ───────────────
stage('Build Docker Image') {
when {
anyOf {
branch 'main'
branch 'release/*'
}
}
steps {
script {
def image = docker.build(
"${DOCKER_REGISTRY}/${APP_NAME}:${IMAGE_TAG}",
"--build-arg BUILD_DATE=${new Date().format('yyyy-MM-dd')} ."
)
// Push to registry
docker.withRegistry("https://${DOCKER_REGISTRY}", 'docker-registry-credentials') {
image.push()
image.push('latest')
}
}
}
}
// ─────────────── Stage 4: Deploy to Staging ───────────────
stage('Deploy to Staging') {
when { branch 'main' }
steps {
withCredentials([file(credentialsId: 'k8s-kubeconfig', variable: 'KUBECONFIG')]) {
sh """
# Update Kubernetes deployment image
kubectl set image deployment/${APP_NAME} \\
${APP_NAME}=${DOCKER_REGISTRY}/${APP_NAME}:${IMAGE_TAG} \\
--namespace=staging
# Wait for rollout
kubectl rollout status deployment/${APP_NAME} \\
--namespace=staging \\
--timeout=5m
"""
}
}
}
// ─────────────── Stage 5: Smoke Test ───────────────
stage('Smoke Test') {
when { branch 'main' }
steps {
sh '''
# Wait for app to be ready
sleep 15
# Run smoke test against staging
curl -f https://staging.example.com/health || exit 1
'''
}
}
// ─────────────── Stage 6: Deploy to Production ───────────────
stage('Deploy to Production') {
when { branch 'main' }
// Require manual approval before deploying to production
input {
message "Deploy to production?"
ok "Yes, deploy!"
parameters {
string(name: 'DEPLOY_REASON', description: 'Why are you deploying?')
}
}
steps {
withCredentials([file(credentialsId: 'k8s-kubeconfig-prod', variable: 'KUBECONFIG')]) {
sh """
kubectl set image deployment/${APP_NAME} \\
${APP_NAME}=${DOCKER_REGISTRY}/${APP_NAME}:${IMAGE_TAG} \\
--namespace=production
kubectl rollout status deployment/${APP_NAME} --namespace=production --timeout=10m
"""
}
}
}
}
// Post-pipeline actions
post {
success {
slackSend(color: 'good', message: "✅ ${APP_NAME} deployed successfully! Build: ${BUILD_NUMBER}")
}
failure {
slackSend(color: 'danger', message: "❌ ${APP_NAME} pipeline FAILED! Build: ${BUILD_NUMBER}")
emailext(
to: '$DEFAULT_RECIPIENTS',
subject: "Jenkins Build Failed: ${JOB_NAME} #${BUILD_NUMBER}",
body: '${JELLY_SCRIPT,template="html"}'
)
}
always {
// Clean workspace
cleanWs()
}
}
}
Jenkins Agents & Nodes
Jenkins agents (formerly called slaves) are machines that run build jobs. The master schedules builds and delegates actual execution to agents.
pipeline {
// Run on any agent
agent any
stages {
stage('Build') {
// Run on a specific labeled agent
agent { label 'linux-build-agent' }
steps { sh 'make build' }
}
stage('Docker Build') {
// Run in a Docker container on the agent
agent {
docker {
image 'node:20-alpine'
args '-v /tmp:/tmp'
}
}
steps {
sh 'npm ci && npm run build'
}
}
stage('K8s Agent') {
// Run in a pod on Kubernetes (cloud agent)
agent {
kubernetes {
yaml '''
apiVersion: v1
kind: Pod
spec:
containers:
- name: maven
image: maven:3.9-jdk-17
command: [cat]
tty: true
'''
defaultContainer 'maven'
}
}
steps {
sh 'mvn clean package'
}
}
}
}
Credentials Management
stage('Use Credentials') {
steps {
// Username/Password credential
withCredentials([usernamePassword(
credentialsId: 'my-credentials',
usernameVariable: 'USERNAME',
passwordVariable: 'PASSWORD'
)]) {
sh 'docker login -u $USERNAME -p $PASSWORD registry.example.com'
}
// Secret text
withCredentials([string(credentialsId: 'api-key', variable: 'API_KEY')]) {
sh 'curl -H "Authorization: Bearer $API_KEY" https://api.example.com'
}
// SSH key
withCredentials([sshUserPrivateKey(
credentialsId: 'deploy-key',
keyFileVariable: 'SSH_KEY'
)]) {
sh 'ssh -i $SSH_KEY deployer@prod.example.com "sudo systemctl restart app"'
}
}
}
Jenkins Best Practices
- Store Jenkinsfile in repository — Treat pipeline as code; version control it alongside your application.
- Use Shared Libraries — Extract common pipeline steps into a shared library to avoid duplication across repos.
- Never hardcode credentials — Always use Jenkins Credentials Store or external secrets management.
- Clean workspace — Use
cleanWs()in post to avoid stale artifacts affecting future builds. - Gate production deploys — Always require manual approval for production deployments (use input{} block).
- Set timeouts — Prevent hanging builds with the
timeoutoption.