Jenkins Pipeline as Code
Introduction¶
Configuring Jenkins jobs through the web UI doesn't version, doesn't review, and
doesn't survive a server rebuild. A Jenkinsfile in the repository does. Modern
Jenkins pipelines come in two flavours — declarative (structured, preferred)
and scripted (raw Groovy, escape hatch).
Declarative pipeline structure¶
pipeline {
agent { label 'linux && docker' }
options {
timeout(time: 30, unit: 'MINUTES')
disableConcurrentBuilds()
buildDiscarder(logRotator(numToKeepStr: '30'))
}
environment {
REGISTRY = 'registry.example.com'
IMAGE = "${REGISTRY}/app:${env.GIT_COMMIT.take(8)}"
}
stages {
stage('Build') {
steps {
sh 'make build'
}
}
stage('Test') {
parallel {
stage('unit') { steps { sh 'make test-unit' } }
stage('lint') { steps { sh 'make lint' } }
}
post {
always { junit 'reports/**/*.xml' }
}
}
stage('Publish') {
when { branch 'main' }
steps {
withCredentials([usernamePassword(
credentialsId: 'registry-creds',
usernameVariable: 'REG_USER',
passwordVariable: 'REG_PASS')]) {
sh 'echo "$REG_PASS" | docker login $REGISTRY -u "$REG_USER" --password-stdin'
sh 'docker push $IMAGE'
}
}
}
}
post {
failure { slackSend channel: '#ci', message: "Failed: ${env.BUILD_URL}" }
cleanup { cleanWs() }
}
}
Key concepts¶
agent— where stages run.agent noneat top level + per-stage agents lets each stage pick its own executor / container.stages/stage/steps— the required nesting. Steps are the actual commands.when— gate a stage on branch, tag, changeset, environment, or an expression.parallel— run stages concurrently; the enclosing stage finishes when all branches do.post—always/success/failure/unstable/changed/cleanupblocks, at pipeline or stage level.environment— env vars; combine withcredentials('id')to bind a secret into a var (still, preferwithCredentialsfor tighter scope).
Credentials¶
Never put secrets in the Jenkinsfile. Store them in Jenkins credentials and bind:
withCredentials([
string(credentialsId: 'aws-oidc-role', variable: 'ROLE_ARN'),
file(credentialsId: 'kubeconfig-prod', variable: 'KUBECONFIG')
]) {
sh 'kubectl apply -f k8s/'
}
Jenkins masks bound credential values in the console. The same caveats apply as any CI (see Handling secrets in CI).
Agents in containers¶
agent {
docker {
image 'node:20'
args '-v $HOME/.cache:/root/.cache'
}
}
Or Kubernetes plugin: each build gets a fresh pod with your defined containers. This keeps the Jenkins controller clean and builds reproducible.
Shared libraries¶
Common logic (deploy steps, notifications, versioning) goes in a shared library
repo, loaded with @Library('acme-ci@main'):
vars/
buildDockerImage.groovy # -> step: buildDockerImage(name: 'app')
src/
com/acme/ci/Deployer.groovy
@Library('acme-ci@main') _
pipeline {
agent any
stages {
stage('build') { steps { buildDockerImage(name: 'app', push: env.BRANCH_NAME == 'main') } }
}
}
Now 40 repos share one implementation and one place to fix a bug.
Verification and troubleshooting¶
- "Scripts not permitted to use method..." — the script security sandbox blocked a Groovy call. Move it into a shared library (trusted), or approve it in Manage Jenkins → In-process Script Approval (sparingly).
- Pipeline hangs on an input/agent — no executor matches the
label, or aninputstep is waiting for a human. Check Build Executor Status. - Credentials empty in the shell —
withCredentialsscope doesn't wrap theshstep, or thecredentialsIdis wrong / not visible to this folder. dockernot found on the agent — the agent label promised Docker but the node lacks it, or the Jenkins user isn't in thedockergroup.- Every build runs on the controller —
agent anywith no separate agents configured. Add agents; keep the controller for orchestration only. - Declarative feels too restrictive — drop into a
script { }block for that one stage rather than converting the whole pipeline to scripted. - Multibranch pipeline not detecting branches — webhook not configured, or the branch source's discovery rules exclude it.
Related tools and reading¶
- On-site: Crontab Generator (for
crontriggers), YAML validator. - Related posts: Handling secrets in CI without leaking them, Blue-green vs canary deployments.
Stuck on something this site can't fix?Reach out to Prabath directly on LinkedIn.More in DevOps & CI/CD
OpenTelemetry for Distributed Tracing
A trace follows one request across services. OpenTelemetry is the vendor-neutral way to produce them — here's the model, the Collector, and sampling that won't bankrupt you.
October 24, 2026pre-commit Hooks That Actually Help
pre-commit runs checks before a commit lands, catching formatting, secrets, and syntax errors locally. Here's a config that helps without being annoying.
October 24, 2026Chaos Engineering Basics
Chaos engineering is controlled experiments that inject failure to verify your system tolerates it. Here's the method, safe blast radius, starter experiments, and game days.
October 24, 2026