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.