Jenkins Pipeline Syntax: A Comprehensive Guide for Beginners
Demystify Jenkins pipeline syntax with this comprehensive guide for beginners. Learn the essentials of Declarative and Scripted pipelines, including agents, stages, steps, post-actions, environments, parameters, and best practices. Empower yourself to build robust CI/CD workflows effectively with practical examples and actionable advice.
Jenkins Pipeline Syntax: A Comprehensive Guide for Beginners
Jenkins Pipeline syntax lets you describe your build, test, and deploy process in a Jenkinsfile that lives with your code. That matters because your pipeline changes can be reviewed, versioned, and rolled back like application code.
If you are new to Jenkins pipelines, start with Declarative Pipeline syntax. It is structured, readable, and covers most CI/CD workflows without requiring much Groovy knowledge.
Declarative vs. Scripted Pipeline
Jenkins supports two pipeline styles.
Declarative Pipeline uses a strict pipeline { ... } structure. It is the best default for most teams because Jenkins can validate the shape of the file and show stages cleanly in the UI.
Scripted Pipeline uses Groovy inside node { ... } blocks. It gives you more control, but it is easier to make hard-to-maintain pipelines if your team is not comfortable with Groovy.
Use Declarative first. Reach for Scripted only when you need logic that Declarative cannot express cleanly.
A Minimal Declarative Pipeline
A basic Jenkinsfile has an agent, stages, and steps:
pipeline {
agent any
stages {
stage('Build') {
steps {
echo 'Building the application'
sh 'mvn clean package'
}
}
stage('Test') {
steps {
sh 'mvn test'
}
}
}
}
agent any tells Jenkins to run the pipeline on any available agent. Each stage appears separately in the Jenkins UI. The steps block contains the commands Jenkins runs.
On Windows agents, use bat instead of sh:
bat 'gradlew.bat test'
The agent Directive
The agent directive decides where the pipeline or stage runs.
Run on any available agent:
agent any
Run on an agent with a specific label:
agent { label 'linux && docker' }
Skip a global agent and choose one per stage:
pipeline {
agent none
stages {
stage('Build') {
agent { label 'linux' }
steps {
sh './build.sh'
}
}
}
}
agent none is useful when different stages need different environments, such as Linux for builds and Windows for installer tests.
Stages and Steps
Use stages to split the pipeline into work a developer can understand at a glance: checkout, build, test, package, deploy.
stages {
stage('Checkout') {
steps {
checkout scm
}
}
stage('Unit Tests') {
steps {
sh 'npm test'
}
}
}
Keep stages meaningful. A stage called Run with fifty commands is hard to debug. A pipeline with twenty tiny stages is noisy. Aim for stages that match real workflow checkpoints.
Environment Variables
Use environment for values that several stages need:
pipeline {
agent any
environment {
APP_NAME = 'orders-api'
IMAGE = "registry.example.com/team/orders-api:${env.BUILD_NUMBER}"
}
stages {
stage('Build Image') {
steps {
sh 'docker build -t "$IMAGE" .'
}
}
}
}
In Groovy strings, you can read variables through env.APP_NAME. In shell steps, Jenkins exports environment variables so $APP_NAME and $IMAGE work inside the shell.
Do not put secrets in environment. Use Jenkins Credentials with withCredentials.
Parameters
Parameters let someone choose values when starting a build manually:
pipeline {
agent any
parameters {
string(name: 'BRANCH_NAME', defaultValue: 'main', description: 'Git branch to build')
booleanParam(name: 'RUN_TESTS', defaultValue: true, description: 'Run unit tests')
choice(name: 'DEPLOY_ENV', choices: ['dev', 'staging', 'prod'], description: 'Target environment')
}
stages {
stage('Show Inputs') {
steps {
echo "Branch: ${params.BRANCH_NAME}"
echo "Deploy target: ${params.DEPLOY_ENV}"
}
}
}
}
Use parameters for things that should vary between runs. Do not use them to bypass review for production changes.
Conditional Stages With when
The when directive controls whether a stage runs:
stage('Deploy to Production') {
when {
branch 'main'
}
steps {
sh './deploy-prod.sh'
}
}
You can also use an expression:
stage('Test') {
when {
expression { return params.RUN_TESTS }
}
steps {
sh 'npm test'
}
}
Put when on the stage, not inside steps.
Post Actions
post blocks run after a stage or pipeline finishes. They are useful for publishing test reports, archiving build evidence, and cleaning up.
pipeline {
agent any
stages {
stage('Test') {
steps {
sh 'mvn test'
}
post {
always {
junit 'target/surefire-reports/*.xml'
}
}
}
}
post {
failure {
echo 'Pipeline failed'
}
always {
cleanWs()
}
}
}
junit publishes test results even when tests fail. cleanWs() comes from the Workspace Cleanup plugin, so install that plugin before using it.
Options
Use options to set pipeline behavior:
pipeline {
agent any
options {
timestamps()
timeout(time: 30, unit: 'MINUTES')
disableConcurrentBuilds()
buildDiscarder(logRotator(numToKeepStr: '20'))
}
stages {
stage('Build') {
steps {
sh './build.sh'
}
}
}
}
These options make logs easier to read, stop hung builds, prevent overlapping runs of the same job, and limit old build history.
A Practical Beginner Pipeline
This example checks out code, builds a Java project, publishes test results, and archives the packaged JAR:
pipeline {
agent { label 'linux' }
options {
timestamps()
timeout(time: 30, unit: 'MINUTES')
disableConcurrentBuilds()
}
stages {
stage('Checkout') {
steps {
checkout scm
}
}
stage('Build') {
steps {
sh 'mvn clean package'
}
}
stage('Publish') {
steps {
junit 'target/surefire-reports/*.xml'
archiveArtifacts artifacts: 'target/*.jar', fingerprint: true
}
}
}
}
This is a better starting point than a giant deploy pipeline. Once the build is reliable, add image publishing, security scans, or deployment stages.
Scripted Pipeline Basics
A simple Scripted Pipeline looks like this:
node('linux') {
stage('Checkout') {
checkout scm
}
stage('Build') {
sh 'mvn clean package'
}
}
Do not wrap a Declarative pipeline {} block inside Scripted Pipeline. Keep the two styles separate unless you have a specific, tested reason to mix small scripted sections inside Declarative script { ... } blocks.
Good Pipeline Habits
Store the Jenkinsfile in source control. Keep secrets in Jenkins Credentials. Give stages names that match the work they do. Publish reports in post blocks so failures still leave useful evidence. Add timeouts before a build hangs overnight.
Most beginner pipeline problems come from hidden assumptions: a tool exists on one agent but not another, a secret is hardcoded, or a report only publishes on success. Make those assumptions explicit in the pipeline, and Jenkins becomes much easier to trust.