A Full CI/CD Pipeline
Build, containerise, push, and deploy β with a human gate before production.
Time to connect everything youβve learned into one real pipeline. Weβll take code, test it, package it as a Docker image, push it to a registry, and deploy it β with an approval step before production. This is a genuine end-to-end CI/CD flow.
π― Learning Objectives
By the end of this lesson you will:
- Build and tag a Docker image inside a pipeline
- Push the image to a registry using credentials
- Add a manual approval gate before production
- Structure a complete CI/CD Jenkinsfile
The Pipeline Stages
A production CI/CD pipeline usually flows like this:
Checkout β Test β Build Image β Push Image β Deploy Staging β [Approve] β Deploy ProdEach stage builds on the last, and the approval gate ensures a human confirms before the highest-risk step.
Building and Pushing a Docker Image
Using an image tagged with the build number gives every build a unique, traceable artifact:
stage('Build Image') {
steps {
sh 'docker build -t myapp:${BUILD_NUMBER} .'
}
}
stage('Push Image') {
steps {
withCredentials([usernamePassword(
credentialsId: 'dockerhub',
usernameVariable: 'USER',
passwordVariable: 'PASS')]) {
sh '''
echo "$PASS" | docker login -u "$USER" --password-stdin
docker push myapp:${BUILD_NUMBER}
'''
}
}
}π‘ Tag with the build number
myapp:${BUILD_NUMBER} gives each build a unique tag, so you always know exactly which image is running and can roll back to a specific one. Avoid deploying latest to production β itβs ambiguous.
The Approval Gate
The input step pauses the pipeline and waits for a human to approve β perfect before a production deploy:
stage('Approve Production') {
steps {
input message: 'Deploy to production?', ok: 'Deploy'
}
}
stage('Deploy Production') {
steps {
sh './deploy.sh production myapp:${BUILD_NUMBER}'
}
}β Guard the input step
By default anyone with access can approve an input. Use the submitter parameter to restrict approval to specific users or groups, so only authorised people can release to production.
Putting It All Together
The complete Jenkinsfile:
pipeline {
agent any
environment { IMAGE = "myapp:${BUILD_NUMBER}" }
stages {
stage('Checkout') { steps { checkout scm } }
stage('Test') { steps { sh 'npm ci && npm test' } }
stage('Build Image') { steps { sh 'docker build -t $IMAGE .' } }
stage('Push Image') {
steps {
withCredentials([usernamePassword(credentialsId: 'dockerhub',
usernameVariable: 'USER', passwordVariable: 'PASS')]) {
sh 'echo "$PASS" | docker login -u "$USER" --password-stdin && docker push $IMAGE'
}
}
}
stage('Deploy Staging') { steps { sh './deploy.sh staging $IMAGE' } }
stage('Approve') { steps { input message: 'Deploy to production?', ok: 'Deploy' } }
stage('Deploy Production') { steps { sh './deploy.sh production $IMAGE' } }
}
post {
failure { echo 'Build failed β notify the team' }
always { sh 'docker logout || true' }
}
}π§ͺ Hands-on Lab
Design a CI/CD Pipeline
- Sketch a Jenkinsfile that tests, builds a Docker image tagged with the build number, and pushes it
- Add a staging deploy, an approval gate, then a production deploy
- Add a
postfailure notification and adocker logoutinalways
π§ Knowledge Check
Why tag a Docker image with the build number instead of just 'latest' in a CI/CD pipeline?
What does the input step do in a Jenkins pipeline?
πΌ Interview Preparation
Describe a complete CI/CD pipeline you've designed or would design in Jenkins.
Summary
Youβve completed the Jenkins track: architecture, setup and jobs, pipelines as code, and now a full CI/CD pipeline with Docker, registry push, and an approval gate. You can automate delivery end to end. Next, we step into the frontier: Agentic AI for DevOps.