CI/CD & Platform Delivery Case Study

From source commit to a healthy Kubernetes workload.

This project builds a complete delivery path for a small Java application: test it, package it, build an immutable container, security-scan it, publish it to a protected registry and deploy it to K3s with health verification and automatic rollback.

  • Jenkins
  • Gradle
  • Docker
  • Trivy
  • K3s
  • Kubernetes

The Challenge

Make delivery safe enough to trust.

Building an image is the easy part. The useful engineering work is in controlling what is allowed to move forward, keeping credentials out of source control, proving each hand-off and making failure recoverable.

The goal was therefore not simply to create a Jenkins job. It was to build a traceable delivery system where a source commit can reach Kubernetes only after tests, packaging and a vulnerability gate have passed, and where a bad deployment returns automatically to the last working image.

Delivery Flow

A controlled path from GitHub to K3s.

Each stage produces evidence before the next stage is allowed to run.

01Test & package

Gradle compiles, tests and archives the Java application.

02Containerise

An isolated Docker-in-Docker builder creates the release image.

03Security gate

Trivy blocks HIGH or CRITICAL findings before publication.

04Publish & deploy

The approved image is pushed to the private registry and rolled out to K3s.

Security Controls

Delivery with deliberately narrow trust boundaries.

Isolated Docker builder

Jenkins talks to a dedicated Docker-in-Docker daemon over TLS rather than receiving TestServer's host Docker socket.

Vulnerability gate

Trivy 0.72.0 scans the image for HIGH and CRITICAL vulnerabilities and returns a failing exit code when policy is breached.

Separate registry identities

Jenkins uses a publishing service account while K3s uses a separate runtime pull identity. Registry access is also firewall-restricted.

Restricted deployment account

Jenkins deploys through a forced SSH command that can invoke only the root-owned Homelab Defender deployment script, not a general shell.

Failure Was Part of the Test

The rollback path was proven before the success path.

Jenkins build 13 completed testing, containerisation, security scanning, registry publication and the Kubernetes rollout. The first service-level health request then received a transient connection reset. The deployment script treated that as a failed release and automatically restored the previous working image.

The health check was then improved to retry safely. Build 14 repeated the complete pipeline, passed the Trivy gate with zero HIGH/CRITICAL findings, published the immutable image and deployed it successfully. Kubernetes rolled out the new pod and the service health check returnedok on the first retry cycle.

Release Evidence

The first fully automated release.

14Jenkins build

First complete hands-off source-to-Kubernetes release.

0HIGH / CRITICAL findings

Trivy gate passed before the image was allowed into the registry.

200Health response

The Kubernetes ClusterIP service returned a healthy application response.

AutoRollback

A failed post-deployment verification restores the previous image automatically.

Engineering Outcome

A delivery pipeline that behaves like an operational system.

The finished pipeline is more than CI. It creates a controlled release chain from GitHub through Gradle, Docker and Trivy into an authenticated registry and finally K3s. The release is not considered successful until the workload has rolled out and the application health endpoint has been verified through the Kubernetes Service.

The project also keeps the Kubernetes baseline, deployment script, rollback procedure, beginner documentation and a living glossary in Git so the implementation remains understandable and reproducible.