How to Run Jenkins on Kubernetes

How to Run Jenkins on Kubernetes

The first time I moved Jenkins from a lonely EC2 instance to Kubernetes, I was skeptical it would be worth the migration effort. A few months in, I was a convert – dynamic build agents that scale to zero when idle, no more manually provisioning beefy static agents, and a controller that survives node failures without me getting paged at 3 a.m. If you’re tired of babysitting Jenkins infrastructure, running it on Kubernetes is one of the highest-leverage changes you can make. Here’s how I set it up, end to end.

Why Run Jenkins on Kubernetes

A traditional Jenkins setup has a controller plus a fixed pool of static agents that sit around consuming resources even when nothing is building. On Kubernetes, Jenkins can dynamically provision agent pods on demand using the Kubernetes plugin, run the build, and tear the pod down the moment the job finishes. This gets you:

  • True elastic scaling – dozens of parallel builds without pre-provisioning machines.
  • Cost efficiency – agents only exist while a build is running.
  • Environment consistency – every build starts from a clean pod image, eliminating “works on my machine” agent drift.
  • Easier disaster recovery – the controller itself becomes just another Kubernetes deployment that can be rescheduled.

Jenkins Architecture on Kubernetes

At a high level, you have:

  • Jenkins Controller – runs as a Deployment or StatefulSet, stores its state (jobs, config, plugins) on a PersistentVolumeClaim, and is exposed via a Service and Ingress.
  • Kubernetes Plugin – installed on the controller, it talks to the Kubernetes API to spin up agent pods as needed, using Pod Templates you define (base image, containers, resource limits).
  • Dynamic Agents – ephemeral pods that register as Jenkins agents over JNLP, run exactly one build, then get deleted automatically.
  • RBAC – a ServiceAccount with permissions scoped to create/delete pods in the target namespace, so Jenkins can manage its own agents securely.

Step 1: Prerequisites

  • A running Kubernetes cluster (EKS, GKE, AKS, or local via Minikube/kind).
  • kubectl configured to talk to the cluster.
  • Helm installed (recommended, though raw manifests work too).

Step 2: Install Jenkins Using Helm

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

kubectl create namespace jenkins

helm install jenkins jenkins/jenkins \
  --namespace jenkins \
  --set controller.serviceType=LoadBalancer \
  --set persistence.enabled=true \
  --set persistence.size=20Gi

This deploys the controller with persistent storage, a default admin user, and the Kubernetes plugin pre-installed. Fetch the initial admin password:

kubectl exec -n jenkins -it svc/jenkins -c jenkins -- \
  /bin/cat /run/secrets/additional/chart-admin-password

Step 3: Set Up RBAC for Dynamic Agents

The Helm chart creates a ServiceAccount and Role automatically, but if you’re doing this manually, here’s the minimum RBAC you need:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: jenkins
  namespace: jenkins
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: jenkins-agent
  namespace: jenkins
rules:
  - apiGroups: [""]
    resources: ["pods", "pods/exec", "pods/log", "persistentvolumeclaims"]
    verbs: ["create", "delete", "get", "list", "watch", "update"]
  - apiGroups: [""]
    resources: ["events"]
    verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: jenkins-agent-binding
  namespace: jenkins
subjects:
  - kind: ServiceAccount
    name: jenkins
    namespace: jenkins
roleRef:
  kind: Role
  name: jenkins-agent
  apiGroup: rbac.authorization.k8s.io

Step 4: Configure the Kubernetes Cloud in Jenkins

Go to Manage JenkinsCloudsAdd a new cloudKubernetes. Set:

  • Kubernetes URL: usually https://kubernetes.default if Jenkins itself runs in-cluster.
  • Kubernetes Namespace: jenkins (or wherever agents should run).
  • Jenkins URL: the internal service URL, e.g. http://jenkins.jenkins.svc.cluster.local:8080.

Click Test Connection to confirm.

Step 5: Define a Pod Template

You can define pod templates in the UI, or better, as code inside your Jenkinsfile using the kubernetes agent directive:

pipeline {
    agent {
        kubernetes {
            yaml """
apiVersion: v1
kind: Pod
spec:
  containers:
  - name: maven
    image: maven:3.9-eclipse-temurin-17
    command: ['sleep']
    args: ['infinity']
    resources:
      requests:
        cpu: "500m"
        memory: "512Mi"
      limits:
        cpu: "1"
        memory: "1Gi"
  - name: docker
    image: docker:24-dind
    securityContext:
      privileged: true
"""
        }
    }

    stages {
        stage('Build') {
            steps {
                container('maven') {
                    sh 'mvn -B clean package'
                }
            }
        }
        stage('Build Image') {
            steps {
                container('docker') {
                    sh 'docker build -t myapp:${BUILD_NUMBER} .'
                }
            }
        }
    }
}

Every stage runs inside a specific container within the same pod, and the whole pod disappears once the pipeline finishes.

Step 6: Expose Jenkins Externally

For production, put Jenkins behind an Ingress with TLS:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: jenkins
  namespace: jenkins
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
  tls:
    - hosts: ["jenkins.mycompany.com"]
      secretName: jenkins-tls
  rules:
    - host: jenkins.mycompany.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: jenkins
                port:
                  number: 8080

Scaling and Resource Management

Set sensible resources.requests and resources.limits on every agent pod template – without them, a runaway build can starve the node and take down other pods. Combine this with a Cluster Autoscaler so the underlying node pool grows and shrinks with build demand instead of you managing node count manually.

Integrating with the Wider Toolchain

Running on Kubernetes makes several other integrations more natural:

  • Docker – use Docker-in-Docker or, better, Kaniko/Buildah for rootless image builds inside pods without privileged mode.
  • Helm/Terraform – deploy stages can run helm upgrade or terraform apply directly from an agent pod that has the right service account bound to cloud IAM roles (via IRSA on EKS, Workload Identity on GKE).
  • Git/GitHub – webhook-triggered builds work the same as any Jenkins setup; just make sure your Ingress is reachable from GitHub for webhook delivery.
  • Monitoring – expose Jenkins’ Prometheus metrics via the Prometheus plugin and scrape them with a ServiceMonitor if you run the kube-prometheus-stack.

Monitoring and Troubleshooting

  • Agent pods stuck in Pending – usually insufficient cluster resources; check kubectl describe pod for scheduling events.
  • “Unable to connect to controller” – check that the JNLP port (default 50000) is exposed via the Jenkins service and reachable from the agent namespace.
  • Persistent volume issues after restart – confirm your StorageClass supports ReadWriteOnce properly and that only one controller pod is scheduled at a time (avoid running the controller as a Deployment with replicas > 1 unless using a proper HA setup).
  • Slow pod startup – pre-pull large images with a DaemonSet or use a private in-cluster registry to cut cold-start time.

Security Best Practices

  • Never run agent containers as privileged: true unless absolutely required (e.g., legacy Docker-in-Docker); prefer Kaniko or Buildah for rootless builds.
  • Restrict the Jenkins ServiceAccount’s RBAC to only the namespace it needs.
  • Store cloud credentials (AWS/GCP/Azure) using Workload Identity or IRSA instead of long-lived static keys baked into pods.
  • Enable Jenkins’ built-in Role-Based Access Control (via the Role-based Authorization Strategy plugin) so not every developer has admin rights to the controller.

Best Practices

  • Keep the controller lightweight – it should orchestrate, not execute heavy builds itself; always delegate work to agent pods.
  • Version your pod templates as YAML in source control, not as UI-only configuration.
  • Use namespace-per-team if multiple teams share one cluster, to isolate resource usage and RBAC.
  • Back up the Jenkins home directory (or use JCasC – Jenkins Configuration as Code – so the whole controller config is reproducible from Git).

FAQs

Can I run Jenkins itself as a pod without Helm? Yes, raw Deployment/Service/PVC manifests work fine; Helm just saves you from writing them by hand.

Do agent pods need Docker installed on the node? Not with Kaniko or Buildah, which build images without a Docker daemon, making them better suited to Kubernetes-native environments.

How do I handle Jenkins upgrades on Kubernetes? Update the image tag in your Helm values and run helm upgrade; because state lives on a PVC, upgrades are generally low-risk if you snapshot the volume first.

Can I run multiple Jenkins controllers on one cluster? Yes, each in its own namespace with its own PVC – useful for team isolation or blue/green controller upgrades.

Is Kubernetes overkill for a small team? If you’re already running Kubernetes for your applications, it’s usually less overhead than maintaining separate static Jenkins agent VMs. If you have no Kubernetes footprint at all, a simpler Docker-based setup might be more proportionate to your needs.

Summary

Running Jenkins on Kubernetes turns your CI/CD infrastructure from a set of static, hand-maintained machines into an elastic, self-healing system. Install the controller via Helm, configure the Kubernetes cloud plugin, define pod templates as code in your Jenkinsfiles, and let the cluster handle scaling agents up and down automatically. Combined with proper RBAC, resource limits, and Workload Identity for cloud credentials, this setup scales from a five-person team to hundreds of concurrent builds without you manually provisioning a single VM.

References

  • Jenkins Kubernetes Plugin documentation: https://plugins.jenkins.io/kubernetes/
  • Official Jenkins Helm chart: https://github.com/jenkinsci/helm-charts
  • Jenkins Configuration as Code (JCasC): https://www.jenkins.io/projects/jcasc/
  • Kubernetes RBAC documentation: https://kubernetes.io/docs/reference/access-authn-authz/rbac/
  • Kaniko (rootless image builds): https://github.com/GoogleContainerTools/kaniko
Total
1
Shares

Leave a Reply

Previous Post
How to Set Up Jenkins for Selenium Testing

How to Set Up Jenkins for Selenium Testing

Next Post
How to Integrate Jenkins with Jira

How to Integrate Jenkins with Jira

Related Posts