5 min read

Running Jenkins on Kubernetes: A Complete Guide

Learn how to deploy Jenkins on Kubernetes using a StatefulSet with dynamic storage and Ingress for external access.

Jenkins is a popular automation server used for building, testing, and deploying code. Running Jenkins on Kubernetes gives you scalable, flexible, and resilient automation pipelines. Whether you’re installing Jenkins for the first time or migrating from a VM, this guide explains every step. We’ll also discuss the pros and cons of this approach.


Table of contents

Introduction

Running Jenkins on Kubernetes means that your Jenkins server runs as a container inside a Kubernetes cluster. This setup automates deployment, scales your agents dynamically, and ensures high availability. In this guide, we explain each step using simple language, making it easy to follow even if you’re new to containers or Kubernetes.

Why Run Jenkins on Kubernetes?

Benefits include:

  • Scalability: Easily add or remove Jenkins agents based on workload.
  • Efficiency: Kubernetes schedules containers optimally to use resources effectively.
  • Resilience: If one pod fails, Kubernetes restarts it, keeping your automation running.

Considerations:

  • Complexity: Learning Kubernetes can be challenging at first.
  • State Management: Persistent storage is essential since Jenkins data must survive pod restarts.

If you want to install Jenkins only for testing purpose and on your local machine - you can check minimal installation of Jenkins.

Prerequisites

Before starting, ensure you have:

  • A Kubernetes cluster up and running (local or cloud-based)
  • kubectl installed and configured to interact with your cluster
  • Basic familiarity with YAML syntax and the command line
  • For Helm-based installations, install Helm v3 on your local machine

Installing Jenkins on Kubernetes

This section details how to deploy Jenkins as a StatefulSet with dynamic storage provisioning and expose it externally using an Ingress resource.

Using YAML Manifests and StatefulSet

Deploying Jenkins as a StatefulSet provides the benefit of stable pod identities and guarantees that data stored on the pod persists even after restarts.

Step 1: Create a Namespace

Namespaces help group related resources. For example, create a namespace called jenkins:

kubectl create namespace jenkins

Step 2: Create a Service Account

Create a file named serviceAccount.yaml:

---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: jenkins-admin
rules:
  - apiGroups: [""]
    resources: ["*"]
    verbs: ["*"]
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: jenkins-admin
  namespace: jenkins
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: jenkins-admin
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: jenkins-admin
subjects:
  - kind: ServiceAccount
    name: jenkins-admin
    namespace: jenkins

Apply it: kubectl apply -f serviceAccount.yaml

Step 3: Set Up Persistent Storage with PVC and StorageClass

Instead of manually creating a PersistentVolume (PV), we let Kubernetes dynamically provision storage using a PersistentVolumeClaim (PVC) along with a preconfigured StorageClass. This means Jenkins will request storage and Kubernetes will automatically provide it based on the storage class settings.

Create a PVC template inside the StatefulSet:

volumeClaimTemplates:
- metadata:
    name: jenkins-data
  spec:
    storageClassName: "jenkins-storage"  # Ensure this StorageClass exists in your cluster for dynamic provisioning.
    accessModes:
      - ReadWriteOnce
    resources:
      requests:
        storage: 10Gi # Modify Storage as per your need

Explanation: Here, jenkins-storage is the StorageClass that instructs Kubernetes how to provision storage. When Jenkins runs, it will create a PVC that dynamically allocates the needed persistent storage, so you only need to manage the claim.

Step 4: Deploy Jenkins as a StatefulSet

Create a file named statefulset.yaml with the following content:

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: jenkins
  namespace: jenkins
spec:
  serviceName: "jenkins"
  replicas: 1
  selector:
    matchLabels:
      app: jenkins-server
  template:
    metadata:
      labels:
        app: jenkins-server
    spec:
      securityContext:
        fsGroup: 1000
        runAsUser: 1000
      serviceAccountName: jenkins-admin
      containers:
      - name: jenkins
        image: jenkins/jenkins:lts
        ports:
        - containerPort: 8080
          name: httpport
        - containerPort: 50000
          name: jnlpport
        volumeMounts:
        - name: jenkins-data
          mountPath: /var/jenkins_home
        livenessProbe:
          httpGet:
            path: "/login"
            port: 8080
          initialDelaySeconds: 90
          periodSeconds: 10
          timeoutSeconds: 5
          failureThreshold: 5
        readinessProbe:
          httpGet:
            path: "/login"
            port: 8080
          initialDelaySeconds: 60
          periodSeconds: 10
          timeoutSeconds: 5
          failureThreshold: 3
  volumeClaimTemplates:
  - metadata:
      name: jenkins-data
    spec:
      storageClassName: "jenkins-storage"  # This storage class handles dynamic provisioning.
      accessModes:
        - ReadWriteOnce
      resources:
        requests:
          storage: 10Gi

Deploy Jenkins kubectl apply -f statefulset.yaml

Step 5: Setting Up Ingress for Jenkins

To make your Jenkins instance accessible from outside the cluster, create an Ingress resource. This example uses an Ingress controller (like NGINX) that rewrites URLs and routes traffic to the Jenkins service.

Create a file named ingress.yaml:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: jenkins-ingress
  namespace: jenkins
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - host: jenkins.bootvar.com  # Replace with your domain or desired hostname.
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: jenkins
            port:
              number: 8080

Create ingress kubectl apply -f ingress.yaml

Note: Ensure you have an Ingress controller running in your cluster. Once configured, navigating to http://jenkins.bootvar.com (or your designated hostname) will route traffic to your Jenkins instance.

Using Helm v3

Helm simplifies deploying Jenkins with pre-configured charts.

Step 1: Add Jenkins Helm Repository

helm repo add jenkinsci https://charts.jenkins.io
helm repo update

Step 2: Prepare an Custom Values File

Create jenkins-values.yaml with your custom values (for example, setting the service type as NodePort, persistent volume details, and required plugins). A sample snippet:

controller:
  ingress:
    enabled: true
    path: /
    hostName: jenkins.bootvar.com

persistence:
  storageClass: "jenkins-pv"
  size: "10Gi"

Step 3: Install Jenkins via Helm

helm install jenkins -n jenkins -f jenkins-values.yaml jenkinsci/jenkins --create-namespace

Step 4: Retrieve admin password

jsonpath="{.data.jenkins-admin-password}"
secret=$(kubectl get secret -n jenkins jenkins -o jsonpath=$jsonpath)
echo $(echo $secret | base64 --decode)

Pros and Cons of Running Jenkins on Kubernetes

Pros

  • Stable Pod Identity: StatefulSet ensures pods have consistent names (e.g., jenkins-0), crucial for data persistence.
  • Dynamic Scaling: Automatically provision and remove Jenkins agents.
  • Resource Efficiency: Kubernetes optimizes hardware usage.
  • High Availability: Kubernetes restarts pods automatically if they fail.

Cons

  • Learning Curve: Kubernetes concepts (StatefulSets, Ingress, PVCs) require time to learn.
  • Configuration Complexity: Setting up RBAC, storage classes, and Ingress resources needs careful planning.
  • Resource Overhead: Kubernetes adds some overhead compared to a single VM deployment.
CI/CD: Running Jenkins Pipelines on Kubernetes Made Simple
In containerization world we run Jenkins pipeline on Kubernetes platform as a pod instead of running it on legacy nodes.

FAQs

  1. Why use a StatefulSet instead of a Deployment?

A StatefulSet ensures that Jenkins retains its identity and attaches to the same storage across restarts. This is crucial for Jenkins as it needs persistent data storage.

  1. Why do we use a Persistent Volume Claim (PVC) and not a Persistent Volume (PV)?

A PVC allows Kubernetes to dynamically provision storage using a StorageClass, eliminating the need for manual PV creation.

  1. Can we scale Jenkins in Kubernetes?

Yes, but typically we scale Jenkins agents rather than the master node. Jenkins agents can be deployed as separate pods that dynamically scale based on job load. In current setup master node cannot be scaled.

  1. How do we ensure Jenkins data persistence in Kubernetes?

By using a StatefulSet and a PVC-backed volume, we ensure that Jenkins data persists across pod restarts and reschedules.

Conclusion

Running Jenkins on Kubernetes brings modern infrastructure benefits—scalability, resilience, and efficiency—to your CI/CD pipelines. As you become comfortable with Kubernetes, you can further customize your deployments, integrate additional plugins, and optimize resource usage.