Opsio - Cloud and AI Solutions
DevOpsCI/CD4 min read· 868 words

How to Build a Jenkins CI/CD Pipeline (Step-by-Step Guide)

Published: ·Updated: ·Reviewed by Opsio Engineering Team
Oscar Bergenbrink

CTO

Technology leadership, cloud architecture, and digital transformation strategy

How to Build a Jenkins CI/CD Pipeline (Step-by-Step Guide)

Jenkins is still the most-deployed CI server in regulated and on-prem enterprise environments, and a working Jenkins pipeline is one of the most useful skills a platform engineer can have. This guide walks through building a real Jenkins pipeline end-to-end: master setup, agent topology, declarative pipeline syntax, secrets handling, and the deploy step. The example produces a Docker image and ships it to a Kubernetes cluster — the most common shape we see in 2026 customer engagements.

Step 1: Pick Your Jenkins Topology

Jenkins has three architecturally distinct deployment modes. Choosing among them is the most consequential decision in the project.

  1. Single-master — one Jenkins controller, one or two agents. Suitable for teams of fewer than 10 engineers.
  2. Master + dynamic agents on Kubernetes — Jenkins controller persistent, agents spun up per build via the Kubernetes plugin. The dominant pattern for medium-to-large teams.
  3. Multi-master with shared agents — multiple controllers (often per-business-unit) sharing an agent pool. Used in very large enterprises with strict tenant isolation.

For 95% of customers we recommend mode 2: a single controller deployed via the official Helm chart on Kubernetes, with the Kubernetes plugin spinning up ephemeral agents per build. Build pods inherit a base image with common tooling baked in (JDK, Maven, Node, Docker buildkit) and are destroyed at the end of each job. This gives you horizontal scale, build isolation, and zero "snowflake agent" drift.

Step 2: Install Jenkins on Kubernetes

The official Helm chart installs the controller, persistent volume, and ingress with one command. The values file is where you customise — set the admin user, plugin list, and Kubernetes-plugin pod template.

helm repo add jenkins https://charts.jenkins.io
helm repo update
helm install jenkins jenkins/jenkins \
  --namespace jenkins --create-namespace \
  --values values.yaml

Critical values to set on day one: controller.adminPassword as a Kubernetes secret reference, controller.installPlugins with at least git, workflow-aggregator, kubernetes, configuration-as-code, credentials-binding, and the language-specific plugins you need. Configuration-as-Code (JCasC) is non-negotiable — it makes the controller's configuration version-controlled and reproducible.

Free Expert Consultation

Need expert help with build a jenkins ci/cd pipeline (step-by-step guide)?

Our cloud architects can help you with build a jenkins ci/cd pipeline (step-by-step guide) — from strategy to implementation. Book a free 30-minute advisory call with no obligation.

Solution ArchitectAI ExpertSecurity SpecialistDevOps Engineer
50+ certified engineersAWS Advanced Partner24/7 support
Completely free — no obligationResponse within 24h

Step 3: Write a Declarative Jenkinsfile

The Jenkinsfile lives in the repo it builds. Below is a working pipeline that builds a Java service, runs tests, builds a Docker image, scans it, and pushes to a registry.

pipeline {
  agent {
    kubernetes {
      yaml '''
        apiVersion: v1
        kind: Pod
        spec:
          containers:
          - name: maven
            image: maven:3.9-eclipse-temurin-21
            command: ['sleep']
            args: ['99d']
          - name: docker
            image: docker:24-dind
            securityContext: { privileged: true }
      '''
    }
  }
  options { timeout(time: 30, unit: 'MINUTES') }
  stages {
    stage('Build') {
      steps { container('maven') { sh 'mvn -B package -DskipTests' } }
    }
    stage('Test') {
      steps { container('maven') { sh 'mvn -B test' } }
      post  { always { junit 'target/surefire-reports/*.xml' } }
    }
    stage('Image') {
      steps {
        container('docker') {
          sh 'docker build -t registry.example.com/api:${GIT_COMMIT} .'
          sh 'trivy image --severity HIGH,CRITICAL registry.example.com/api:${GIT_COMMIT}'
        }
      }
    }
    stage('Publish') {
      when { branch 'main' }
      steps {
        withCredentials([usernamePassword(credentialsId: 'registry',           usernameVariable: 'U', passwordVariable: 'P')]) {
          container('docker') {
            sh 'docker login -u $U -p $P registry.example.com'
            sh 'docker push registry.example.com/api:${GIT_COMMIT}'
          }
        }
      }
    }
  }
}

Step 4: Wire Up Secrets and Credentials

Never put credentials in the Jenkinsfile. Use the Credentials plugin (built-in) to store registry passwords, deploy keys, and cloud-provider tokens, and reference them with withCredentials blocks. For production we strongly recommend pairing this with HashiCorp Vault — the hashicorp-vault-plugin lets Jenkins fetch short-lived dynamic secrets per build instead of holding long-lived credentials in the controller.

Step 5: Add the Deploy Stage (GitOps Recommended)

The traditional Jenkins approach is to deploy directly: install kubectl in the agent and run kubectl apply. We recommend against this. Instead, use the Opsio's argocd gitops handoff pattern: Jenkins commits an updated image tag to a separate config repo, ArgoCD watches that repo and reconciles the cluster. This keeps Kubernetes credentials out of Jenkins entirely.

stage('Update Manifests') {
  when { branch 'main' }
  steps {
    withCredentials([sshUserPrivateKey(credentialsId: 'config-repo-key',       keyFileVariable: 'KEY')]) {
      sh '''
        git clone git@github.com:acme/k8s-config.git
        cd k8s-config
        yq eval -i '.image.tag = "${GIT_COMMIT}"' apps/api/values.yaml
        git -c user.email=ci@acme.com -c user.name=ci commit -am "api: ${GIT_COMMIT}"
        git push
      '''
    }
  }
}

Step 6: Make It Production-Grade

A pipeline that works on a sunny day is not a production pipeline. Add the following before declaring victory:

  • Build retention — keep last 50 builds and last 7 days, prune older. Disk fills fast otherwise.
  • Pipeline tests — Jenkinsfile syntax tested with jenkins-pipeline-unit in CI.
  • Webhook health — monitor PR-trigger latency. Stale webhooks kill developer trust.
  • Backup — JCasC config in Git plus daily backup of $JENKINS_HOME to S3.
  • Upgrade cadence — Jenkins LTS upgrades quarterly, plugin upgrades monthly with regression testing.

How Opsio Helps

Opsio runs Jenkins as a managed platform for customers in financial services, healthcare, and industrial automation — sectors where on-prem isolation and auditability matter more than SaaS convenience. Our jenkins automation services service covers controller upgrades, plugin curation, JCasC config, agent topology, and the GitOps handoff to ArgoCD. We also handle the managed pipeline services redesign for customers who want to stay on Jenkins but adopt modern delivery patterns.

About the Author

Oscar Bergenbrink
Oscar Bergenbrink

CTO

Technology leadership, cloud architecture, and digital transformation strategy

Editorial standards: This article was written by a certified practitioner and peer-reviewed by our engineering team. We update content quarterly to ensure technical accuracy. Opsio maintains editorial independence — we recommend solutions based on technical merit, not commercial relationships.