Build Your First Jenkins Declarative Pipeline for CI/CD
Jenkins is the de facto standard for open-source automation servers, serving as the backbone for continuous integration and continuous delivery (CI/CD) pipelines across thousands of organizations. While Jenkins offers two types of pipeline syntax—Scripted and Declarative—the Declarative Pipeline syntax is the recommended approach for most users due to its simpler, structured, and more readable Groovy DSL.
This guide provides a comprehensive, step-by-step tutorial on building your very first functional Jenkins Declarative Pipeline. We will cover integrating source code management (SCM), defining the build environment, executing testing, and implementing a sample deployment stage, giving you a strong foundation for automating your software delivery lifecycle.
Understanding the Declarative Pipeline Structure
The Declarative Pipeline defines the entire workflow in a file named Jenkinsfile, which is stored alongside your application code in your SCM repository (e.g., Git). This practice is known as Pipeline as Code.
Core Elements of a Declarative Pipeline
A Declarative Pipeline must contain the following top-level blocks:
pipeline: The mandatory outermost block defining the pipeline content.agent: Specifies where the pipeline or a specific stage will execute (e.g.,any,none, or specific labels).stages: Contains one or more sequentialstageblocks.stage: Defines a conceptual block of work (e.g., Build, Test, Deploy).steps: Contains one or more commands or functions executed within astage.
pipeline {
agent any
environment { // Optional: Defines environment variables
APP_VERSION = '1.0.0'
}
stages {
stage('Build') {
steps {
// Commands go here
}
}
}
post { // Optional: Actions run after the pipeline completes
always {
echo 'Pipeline finished.'
}
}
}
Step 1: Setting Up the Jenkins Job
To run a Pipeline from code, you need to configure a Jenkins job to read the Jenkinsfile from your repository.
- Navigate to your Jenkins dashboard and select New Item.
- Enter a name for your pipeline (e.g.,
my-first-ci-pipeline). - Select the Pipeline item type and click OK.
- In the configuration page, scroll down to the Pipeline section.
- Change the definition from Pipeline script to Pipeline script from SCM.
- Select your SCM (e.g., Git).
- Enter the Repository URL and configure your credentials if necessary.
- Ensure the Script Path is set to
Jenkinsfile(the default).
Step 2: Defining the Jenkinsfile for CI/CD
We will create a comprehensive Jenkinsfile that simulates a simple CI/CD workflow, integrating standard stages for a successful deployment.
Assume you have a repository structure containing your application source code (e.g., a simple Python or Java project) and the Jenkinsfile at the root.
The Complete Declarative Pipeline Example
This pipeline uses the node agent (if configured) and utilizes basic shell steps (sh) to simulate real work. The deployment stage is conditional, running only upon successful completion of the previous stages.
// Jenkinsfile
pipeline {
// 1. Agent Definition: Specifies where the entire pipeline runs
agent {
label 'my-build-agent' // Use a specific label if available, or 'any'
}
// 2. Environment Variables: Define variables usable throughout the pipeline
environment {
CONTAINER_REGISTRY = 'registry.example.com'
IMAGE_NAME = 'myapp'
}
stages {
// Stage 1: Checkout (Source Code Management)
stage('Checkout Source') {
steps {
// The 'checkout scm' step automatically checks out the code
// based on the configuration defined in the Jenkins job settings.
checkout scm
sh 'echo "Source code successfully checked out."
}
}
// Stage 2: Build Artifacts
stage('Build Artifact') {
steps {
sh 'echo "Starting build for $IMAGE_NAME:$BUILD_ID"'
// Simulation: Build a Docker image or compile a jar/war file
sh "docker build -t ${IMAGE_NAME}:${BUILD_ID} ."
}
}
// Stage 3: Testing and Quality Gates
stage('Run Tests') {
steps {
sh './run_unit_tests.sh'
sh 'echo "Running integration tests..."'
// Example of collecting test results (requires appropriate plugins)
// junit '**/target/surefire-reports/*.xml'
}
}
// Stage 4: Deployment (Conditional)
stage('Deploy to Staging') {
// The 'when' directive ensures the stage only runs under specific conditions
when {
branch 'main'
}
steps {
sh "docker push ${CONTAINER_REGISTRY}/${IMAGE_NAME}:${BUILD_ID}"
sh 'kubectl apply -f k8s/deployment-staging.yaml'
script {
// Script blocks allow traditional Groovy logic within Declarative steps
echo "Deployment successful to Staging environment."
}
}
}
}
// 3. Post Actions: Define actions based on the final pipeline status
post {
success {
echo 'Pipeline completed successfully. Notifying team via Slack...'
// slackSend channel: '#devops-alerts', message: 'CI/CD Success!'
}
failure {
echo 'Pipeline failed. Review logs for errors.'
}
cleanup {
sh 'docker rmi -f $(docker images -aq --filter label=build_id=$BUILD_ID)'
}
}
}
Step 3: Running and Monitoring the Pipeline
Once the Jenkinsfile is committed and pushed to the branch configured in your Jenkins job:
- On the Jenkins job page, click Build Now (or wait for the SCM polling/webhook trigger).
- Monitor the build in the Build History panel.
- Click the running build number and select Console Output to view the execution details step-by-step.
- You can also use the Stage View visualization (if the plugin is installed) to see the progress of the
Checkout,Build,Test, andDeploystages graphically.
Best Practices and Advanced Features
Utilizing Libraries and Shared Code
For complex or highly repetitive steps, avoid writing verbose Groovy directly in the Jenkinsfile. Instead, use Shared Libraries. These external libraries allow you to define common functions (like standard deployment routines or notification functions) that can be called from multiple pipelines, making your Jenkinsfile cleaner.
// Calling a shared library step
stage('Custom Setup') {
steps {
customLibrary.initializeEnvironment(env: 'prod')
}
}
Working with Credentials
Never hardcode sensitive information (passwords, tokens, API keys) directly into the Jenkinsfile. Use Jenkins' built-in Credentials Management system.
The withCredentials step allows you to securely access stored secrets:
stage('Authenticate Registry') {
steps {
withCredentials([usernamePassword(credentialsId: 'docker-registry-creds',
passwordVariable: 'PASS',
usernameVariable: 'USER')]) {
sh "docker login -u ${USER} -p ${PASS} ${CONTAINER_REGISTRY}"
}
}
}
Warning: Agent Selection
Tip: Always define your
agentspecifically using alabelrather thanagent any. Usingagent anymeans the pipeline could run on the Jenkins controller node, which is a security risk and can impact controller performance. Ensure you have properly configured Jenkins agents (nodes) with necessary tools (Docker, Maven, Node.js) installed.
Conclusion
The Jenkins Declarative Pipeline offers a powerful, readable, and maintainable framework for implementing Continuous Integration and Continuous Delivery. By defining the workflow structure—including agents, environment variables, sequential stages, and post-build actions—you gain complete visibility and control over your software automation process. With this foundational Jenkinsfile established, you are now ready to integrate more complex steps, such as automated security scanning, artifact publishing, and multi-environment promotions.