Jenkins Declarative Pipelines from Scratch

Declarative Pipelines are the most surprising way to write Jenkins pipelines because they hide imperative logic behind a structured, opinionated syntax, making them easier to read and write but harder to debug when things go wrong under the hood.

Let’s see one in action. Imagine a simple pipeline that checks out code, runs some tests, and then deploys the result.

pipeline {
    agent any

    stages {
        stage('Checkout') {
            steps {
                checkout scm
            }
        }
        stage('Test') {
            steps {
                sh 'mvn clean test'
            }
        }
        stage('Deploy') {
            steps {
                script {
                    if (env.BRANCH_NAME == 'main') {
                        echo 'Deploying to production...'
                        // Actual deployment steps would go here
                    } else {
                        echo 'Not deploying to production.'
                    }
                }
            }
        }
    }
}

This pipeline defines a clear sequence of stages: Checkout, Test, and Deploy. The agent any directive means Jenkins can run this pipeline on any available agent. Inside each stage, steps contain the actual commands to execute. Notice the script block within the Deploy stage – this is where you can drop down into more imperative Groovy code when the declarative syntax isn’t expressive enough. The env.BRANCH_NAME variable is automatically populated by Jenkins and is crucial for conditional logic like deciding whether to deploy to production.

The problem this solves is the chaos of "scripted" Jenkins pipelines, which were essentially arbitrary Groovy scripts. This made them powerful but incredibly difficult for teams to manage, version, and understand. Declarative pipelines bring structure, enforcing a predictable format that makes pipelines easier to read, write, and maintain. They use a set of predefined keywords and structures, ensuring consistency across your organization.

Internally, when Jenkins processes a Declarative Pipeline, it first parses the Jenkinsfile and converts it into an internal "runnable" representation. This conversion process is where the declarative structure is translated into executable steps. The agent, stages, stage, and steps keywords are all part of this structured definition. The script block acts as a gateway to the underlying Scripted Pipeline syntax, allowing you to embed imperative logic when necessary.

The exact levers you control are primarily through the structure and keywords available in the Declarative Pipeline syntax. You define the agent (where the pipeline runs), the stages (logical divisions of work), and the steps within each stage. You can also define environment variables, parameters for manual triggering, triggers for automatic execution, post actions for cleanup or notifications, and options to configure pipeline behavior.

When you define a when clause within a stage, Jenkins doesn’t just check a condition at the start of the stage. Instead, it evaluates the when condition before executing the steps within that stage. If the condition is false, Jenkins skips the entire stage and marks it as "skipped" in the build history, rather than failing the build. This behavior is fundamental to how Declarative Pipelines manage conditional execution flow, preventing unexpected failures when certain criteria aren’t met.

The next concept you’ll encounter is how to manage credentials securely within your pipelines.

Want structured learning?

Take the full Jenkins course →