Jenkins Pipeline Stages, Steps, and Agents
Stages, steps, and agents are the three structural pillars of every Jenkins Pipeline. Understanding each one clearly helps you design pipelines that are organized, efficient, and easy to troubleshoot.
Stages: The Phases of Your Pipeline
A stage represents a logical phase of the pipeline — a distinct chunk of work with a clear purpose. Stages appear as visual boxes in the Jenkins Stage View, making it immediately obvious which phase passed, which failed, and how long each took.
Why Stages Matter
Pipeline without stages (all commands in one block): Build → ??? (Hard to know where failure occurred) Pipeline with stages (clear phases): Checkout → Build → Unit Tests → Code Scan → Package → Deploy Console shows: [Checkout] ✓ 12s [Build] ✓ 45s [Unit Tests] ✗ 3m 12s ← FAILED HERE [Code Scan] — (skipped) [Package] — (skipped) [Deploy] — (skipped)
With stages, you immediately know the test phase failed after 3 minutes. Without stages, you have to read through all the console output to find the error.
Stage Best Practices
Good stage naming:
stage('Build') ← Clear, short
stage('Unit Tests') ← Specific
stage('Deploy to Staging') ← Destination is clear
stage('Smoke Tests') ← Test type is clear
Bad stage naming:
stage('Do Stuff') ← Vague
stage('Step 3') ← No meaning
stage('Build and Test and Deploy and Send Email') ← Too many things
Steps: The Individual Commands
A step is one action inside a stage. A stage typically contains multiple steps that run in sequence. Steps are the actual work — running commands, checking out code, archiving files.
Step Types Reference
| Step | Example | What It Does |
|---|---|---|
| sh | sh 'npm install' | Run a shell command on Linux/Mac |
| bat | bat 'npm install' | Run a Windows batch command |
| echo | echo 'Starting build' | Print to the console log |
| git | git url: '...', branch: 'main' | Checkout a Git repository |
| checkout scm | checkout scm | Checkout using the job's SCM configuration |
| archiveArtifacts | archiveArtifacts 'target/*.jar' | Save files as downloadable artifacts |
| junit | junit 'reports/*.xml' | Publish test results to Jenkins |
| timeout | timeout(time:5, unit:'MINUTES') | Fail if a step takes too long |
| retry | retry(3) { sh 'flaky-test.sh' } | Retry a step up to N times on failure |
| input | input 'Deploy to production?' | Pause and wait for human approval |
| sleep | sleep 60 | Wait for N seconds |
| withEnv | withEnv(['VAR=value']) { sh '...' } | Set env variables for wrapped steps |
| withCredentials | withCredentials([...]) { sh '...' } | Inject secrets for wrapped steps |
| stash | stash name: 'built-app', includes: 'target/**' | Save files to pass between stages/nodes |
| unstash | unstash 'built-app' | Retrieve stashed files |
Stash and Unstash: Sharing Files Between Stages
When stages run on different agents, files do not automatically carry over. Use stash to save files after one stage and unstash to retrieve them in another.
Diagram: Stash and Unstash
Stage: Build (runs on agent-1)
sh 'mvn package'
stash name: 'jar-file', includes: 'target/*.jar'
|
| (file saved in Jenkins controller)
↓
Stage: Deploy (runs on agent-2)
unstash 'jar-file'
sh 'deploy.sh target/*.jar'
Agents: Where Stages Run
An agent is the machine (or container) that executes a stage. You define agents at the pipeline level (applies to all stages) or at the stage level (overrides for that stage only).
Pipeline-Level Agent
pipeline {
agent any // All stages run on any available node
...
}
pipeline {
agent { label 'linux-large' } // All stages run on a "linux-large" labeled node
...
}
pipeline {
agent none // No default — every stage must specify its own agent
...
}
Stage-Level Agent
Override the agent for specific stages. This is useful when different stages need different environments — for example, build on a Linux server and deploy from a Windows server.
pipeline {
agent none
stages {
stage('Build') {
agent { label 'linux-maven' }
steps {
sh 'mvn clean package'
stash name: 'app-jar', includes: 'target/*.jar'
}
}
stage('Test on Windows') {
agent { label 'windows-test' }
steps {
unstash 'app-jar'
bat 'run-tests.bat'
}
}
stage('Deploy') {
agent { label 'deploy-server' }
steps {
unstash 'app-jar'
sh 'deploy.sh'
}
}
}
}
Docker as Agent
Run a stage inside a Docker container. The container starts, runs the steps, and is destroyed afterward. No setup required on the host machine for language runtimes.
stage('Build with Maven 3.9') {
agent {
docker {
image 'maven:3.9-eclipse-temurin-17'
args '-v /root/.m2:/root/.m2' // Mount Maven cache
}
}
steps {
sh 'mvn clean package'
}
}
stage('Build with Node 20') {
agent {
docker { image 'node:20-alpine' }
}
steps {
sh 'npm ci && npm run build'
}
}
Diagram: Different Docker containers per stage
Stage 1: Build Java
[ maven:3.9 container ]
→ mvn clean package
→ Container destroyed
Stage 2: Build Frontend
[ node:20 container ]
→ npm run build
→ Container destroyed
Stage 3: Security Scan
[ owasp/dependency-check container ]
→ dependency-check.sh
→ Container destroyed
Each stage gets a fresh, clean environment.
The Manual Approval Step
The input step pauses the pipeline and waits for a human to click Proceed or Abort. Use this before deploying to production environments.
stage('Deploy to Production') {
steps {
input message: 'Deploy version 2.14 to production?',
ok: 'Yes, deploy now',
submitter: 'deploy-approvers'
// Only runs if the approver clicked "Yes, deploy now"
sh 'kubectl apply -f k8s/production/'
}
}
The submitter field restricts who can approve the deployment. Only users in the deploy-approvers group or user list can click Proceed.
Key Points
- Stages group related steps into named phases — they appear as visual boxes in Jenkins Stage View.
- Steps are individual commands: shell scripts, git checkout, archive files, publish tests, and more.
- Use stash and unstash to pass files between stages running on different agents.
- Agents define where stages run — any node, a labeled node, or a Docker container.
- The input step pauses the pipeline for human approval before proceeding to critical stages like production deployment.
