Jenkins on k3s With Persistent State
Helm said deployed, the pod said Init:CrashLoopBackOff. Reading the init container's logs found a volume-ownership issue; Jenkins now survives reboots.
Context
devops-k3s-01 is a single-node k3s cluster (Ubuntu 24.04, Traefik, local-path storage) and the base of CyberDen's DevSecOps platform. Gitea, its bundled PostgreSQL and Valkey were already running on bound PVCs. Jenkins was next, with its home directory on a dedicated 100 GB virtual disk backed by the T320's ZFS pool, so CI state outlives the pod.
The install
helm install jenkins jenkins/jenkins \
--namespace devsecops \
--values ~/cyberden/platform/jenkins-values.yaml
kubectl get pods -n devsecops -w
Helm reported STATUS: deployed. The pod reported:
jenkins-0 0/2 Init:CrashLoopBackOff

Reading the right logs
deployed only means Kubernetes accepted the resources. The failure was in an init container, so the main container's logs were empty. The useful commands:
kubectl get pod jenkins-0 -n devsecops \
-o jsonpath='{.status.initContainerStatuses[*].name}{"\n"}'
kubectl logs -n devsecops jenkins-0 -c init --previous
kubectl describe pod jenkins-0 -n devsecops | tail -80
Root cause
The Jenkins image runs as UID/GID 1000. The directory backing the persistent volume on the node was owned by root, so the init container couldn't write plugins or configuration into JENKINS_HOME. It crashed, restarted and crashed again.
Fix
- Changed ownership of the backing directory to
1000:1000 - Deleted only the pod. The StatefulSet recreated it, and the PVC and data were untouched:
kubectl delete pod jenkins-0 -n devsecops
The pod then walked through the lifecycle I was waiting for:
Init:0/2 → Init:1/2 → PodInitializing → 1/2 Running → 2/2 Running
2/2 is the Jenkins controller plus the config-reload sidecar.
Proving persistence
After a VM reboot: k3s starts → the StatefulSet restores Jenkins → the PVC mounts the same data → Jenkins comes up with its configuration intact instead of re-initializing. That's the property that matters for a CI server.
Lessons
- Helm status is not health. Watch the pod lifecycle.
- Init containers fail quietly. Use
-c <init> --previous. - Know your image's UID before you hand it a hostPath or local-path volume.
- Delete pods, not releases, when the StatefulSet already owns the fix.
Sources: Kubernetes Cluster for AI VM (2 Aug 2026), DevSecOps Kubernetes Plan (6 Aug 2026)