Skip to main content

Jenkins

Jenkins is an open-source automation server widely used in enterprise environments for CI/CD pipelines. It uses a Groovy-based Jenkinsfile to define declarative pipelines with stages, agents, and post-build actions — running on self-hosted infrastructure with full customisation.

Understanding Jenkins

What Is Jenkins in Simple Terms

Jenkins is the granddaddy of CI/CD servers. It has been running pipelines since 2011 and is still the dominant CI platform in large enterprise environments — banks, telcos, insurance companies, and any organisation that cannot use cloud-hosted CI for compliance reasons. Many Indian enterprises running on-premise infrastructure use Jenkins as their primary automation server.

The modern way to use Jenkins is through declarative Jenkinsfiles — pipeline configuration stored in the repository alongside the code, not configured through a UI.

How It Works

◈ DIAGRAM
+------------------------------------------+
| Jenkins Controller |
| Web UI, job scheduling, plugin mgmt |
| Stores build history, credentials |
+------------------------------------------+
|
distributes jobs to agents
|
+---------+---------+
| | |
v v v
+----------+ +----------+ +----------+
| Agent 1 | | Agent 2 | | Agent 3 |
| Java | | Node.js | | Docker |
| builds | | builds | | builds |
+----------+ +----------+ +----------+
Or: dynamic Kubernetes agents
Each build gets a fresh pod
Pod terminates after build

Declarative Jenkinsfile:

GROOVY
// Jenkinsfile
pipeline {
agent none // no global agent -- defined per stage
environment {
ECR_REGISTRY = '123456789.dkr.ecr.ap-south-1.amazonaws.com'
IMAGE_NAME = 'payment-api'
// Bind Jenkins credential to env var
AWS_CREDENTIALS = credentials('aws-ecr-credentials')
}
stages {
stage('Build') {
agent { docker { image 'docker:24-dind' } }
steps {
script {
docker.build("${ECR_REGISTRY}/${IMAGE_NAME}:${GIT_COMMIT}")
docker.withRegistry("https://${ECR_REGISTRY}") {
docker.image("${IMAGE_NAME}:${GIT_COMMIT}").push()
}
}
}
}
stage('Test') {
// Run test and lint in parallel
parallel {
stage('Unit Tests') {
agent { docker { image 'node:20-alpine' } }
steps {
sh 'npm ci'
sh 'npm test'
junit 'test-results/*.xml'
}
}
stage('Lint') {
agent { docker { image 'node:20-alpine' } }
steps {
sh 'npm ci'
sh 'npm run lint'
}
}
}
}
stage('Deploy Staging') {
agent { label 'k8s-deployer' }
when {
branch 'main' // only on main branch
}
steps {
sh "./deploy.sh staging ${GIT_COMMIT}"
}
}
stage('Deploy Production') {
agent { label 'k8s-deployer' }
when { branch 'main' }
// Pause and wait for human input
input {
message 'Deploy to production?'
ok 'Deploy'
submitter 'tech-leads'
}
steps {
sh "./deploy.sh production ${GIT_COMMIT}"
}
}
}
post {
success {
slackSend channel: '#deployments',
message: "SUCCESS: ${env.JOB_NAME} #${env.BUILD_NUMBER}"
}
failure {
slackSend channel: '#deployments',
color: 'danger',
message: "FAILED: ${env.JOB_NAME} #${env.BUILD_NUMBER}"
}
always {
cleanWs() // clean workspace after every build
}
}
}

Practical Commands

Bash
## Jenkins CLI
java -jar jenkins-cli.jar -s http://jenkins.internal \
-auth admin:API_TOKEN build payment-api-pipeline
## Trigger build with parameters
java -jar jenkins-cli.jar -s http://jenkins.internal \
build payment-api-pipeline \
-p ENVIRONMENT=staging \
-p VERSION=v1.2.3
## Get build log
java -jar jenkins-cli.jar -s http://jenkins.internal \
console payment-api-pipeline 42 ## build number 42
## Restart Jenkins safely
java -jar jenkins-cli.jar -s http://jenkins.internal safe-restart

Troubleshooting

Symptom Check What to Look For
Build stuck in queue Agent availability Agents offline or label mismatch
Credentials not found Manage Jenkins > Credentials Credential ID matches Jenkinsfile
Pipeline syntax error Replay > edit and retry Groovy syntax in Jenkinsfile
Workspace conflicts Parallel build settings Enable concurrent build isolation
Tip

Use the Jenkins Pipeline Syntax generator (available at http://jenkins.internal/pipeline-syntax/) to generate correct Groovy syntax for any plugin step. It is an interactive UI that produces snippet code — far faster than reading plugin documentation.

Remember

Always use agent none at the pipeline level and declare agents per stage. Using a global agent means all stages run on the same machine sequentially. Per-stage agents allow parallel execution on different machines and Docker containers — which is how modern Jenkins pipelines run efficiently.

Security

Store all credentials in the Jenkins Credentials Store — never hardcode passwords, API keys, or tokens in the Jenkinsfile. Use credentials() binding to inject them as environment variables. The Jenkinsfile is committed to Git and visible to anyone with repository access.

Common Mistake

Using scripted pipelines instead of declarative. Scripted pipelines are raw Groovy — powerful but fragile, hard to read, and difficult to validate. Declarative pipeline syntax is structured, validated before execution, and supports the full range of Jenkins features. Always start with declarative unless you have a specific reason that requires scripted.

Frequently Asked Questions

Why do teams still choose self-hosted Jenkins over hosted CI/CD like GitHub Actions or GitLab CI?

Jenkins predates modern hosted CI by over a decade (first released in 2011 as a Hudson fork) and its main draw now is its plugin ecosystem — over 1,800 plugins covering nearly any tool integration, legacy system, or custom build requirement — plus full control over the build infrastructure, which matters for regulated environments that can't send build data to a third-party SaaS. The tradeoff is you own patching, scaling agents, and securing the Jenkins master yourself.

What's a common operational mistake with Jenkins pipelines in production?

Running builds directly on the Jenkins controller node instead of dedicated agents — this couples build workload to the same machine serving the web UI and orchestrating all other jobs, so one runaway build can degrade or crash the whole instance for every team using it. Best practice is agent-based execution (static or dynamic, e.g. Kubernetes pod agents) so the controller only orchestrates, never executes build steps itself.