Jenkins pipelines offer a powerful way to automate complex workflows, and the post section is where you define actions that run after the main pipeline steps have completed, regardless of their outcome. This isn’t just about logging; it’s about robust error handling, resource cleanup, and sending notifications.

Let’s see it in action. Imagine a pipeline that builds a Docker image, pushes it to a registry, and then deploys it. We want to know if anything went wrong, clean up any temporary files, and notify folks on Slack.

pipeline {
    agent any
    stages {
        stage('Build Docker Image') {
            steps {
                sh 'docker build -t my-app:latest .'
            }
        }
        stage('Push Docker Image') {
            steps {
                sh 'docker push my-registry/my-app:latest'
            }
        }
        stage('Deploy') {
            steps {
                sh './deploy.sh'
            }
        }
    }
    post {
        always {
            echo 'Pipeline finished. Performing cleanup.'
            // Example cleanup: delete temporary files
            sh 'rm -rf /tmp/build_artifacts/*'
        }
        success {
            echo 'Pipeline succeeded! Deployments are live.'
            // Example success notification
            slackSend channel: '#deployments', message: 'Build and deploy of my-app succeeded!'
        }
        failure {
            echo 'Pipeline failed! Investigating issues.'
            // Example failure notification
            slackSend channel: '#deployments', message: 'Build and deploy of my-app FAILED! Check Jenkins logs.'
        }
        unstable {
            echo 'Pipeline finished with unstable status (e.g., tests failed but build continued).'
            // Example unstable notification
            slackSend channel: '#deployments', message: 'Build and deploy of my-app is UNSTABLE. Check test results.'
        }
        changed {
            echo 'Pipeline status changed from its previous state.'
        }
    }
}

The post section contains several blocks, each triggered by a specific pipeline outcome:

  • always: This block always runs, no matter if the pipeline succeeded, failed, or was aborted. It’s your go-to for cleanup tasks that must happen every time, like removing temporary files, deleting Docker images, or closing database connections.
  • success: Executes only when all stages in the pipeline complete successfully. This is perfect for sending success notifications, merging code, or triggering downstream jobs that depend on a successful build.
  • failure: Runs exclusively when one or more stages in the pipeline fail. This is critical for alerting the right people, creating bug tickets, or performing rollback actions.
  • unstable: Triggers if the pipeline has an "unstable" status. This often occurs when tests fail but the build itself doesn’t completely break. It’s useful for notifying teams about test failures without necessarily indicating a full pipeline breakdown.
  • changed: This block runs if the pipeline’s status has changed from the previous run. For example, if a pipeline that was previously successful now fails, the changed block will execute. This is good for detecting regressions or improvements.
  • aborted: Executes if the pipeline is manually aborted.

The most surprising truth about Jenkins post sections is that they are evaluated after the entire pipeline run, including any stages. This means that even if a stage fails critically, the post section’s always block will still attempt to execute, providing a guaranteed exit strategy for essential tasks.

The post section is evaluated in a specific order. always is checked last, meaning its steps execute after all other post conditions have been evaluated. This makes it ideal for final cleanup. success, failure, unstable, changed, and aborted are evaluated in the order they appear in the post block, and only the first matching condition will trigger its steps. If you have success and changed, and the pipeline succeeds, only success will run. If you want always to run before other conditions, you’d need to structure your post section differently or use a separate post block.

Understanding the execution order and the specific conditions is key. For instance, if you have both success and changed in your post block, and the pipeline runs successfully after a previous failure, only the success block will execute. The changed block, which might also seem applicable, is skipped because success is evaluated first and matches.

When dealing with complex post conditions, especially if you need to run certain cleanup tasks before success or failure notifications, you can define multiple post blocks. Jenkins processes them sequentially. For example, to ensure a specific cleanup always happens before any notification, regardless of success or failure, you’d place an always block first.

The real power comes from combining these conditions with specific actions. You can use sh for shell commands, bat for Windows batch, call other Jenkins jobs, or integrate with external services like Slack, email, or ticketing systems. The post section is not just a simple callback; it’s a fundamental part of building resilient and informative CI/CD pipelines.

If you’re looking to coordinate more intricate multi-pipeline workflows based on the success or failure of your current pipeline, you’ll want to explore the build.result variable within your post conditions.

Want structured learning?

Take the full Jenkins course →