ADR 0009 — Jenkins single-VM controller

One Ubuntu 24.04 VM running Jenkins LTS on OpenJDK 21, with HAProxy as the TLS termination point and Jenkins itself reachable only from the HAProxy edge.

Date: 2026-05-08 Status: Accepted.

Context

The operator explicitly requested a Jenkins installation on an Ubuntu cloud-init VM, exposed through the existing PowerDNS / HAProxy / Let’s Encrypt edge path defined by ADR 0005.

This workspace remains OpenShift-first. Jenkins is accepted here only as a supporting lab automation service for build and platform workflows. It is not a general application-catalogue entry, and it does not get OpenShift-platform GitOps treatment. The federated GitOps architecture (ADR 0015) places Jenkins under vm-platform-ops, not under openshift-platform-gitops.

The reason for choosing Jenkins specifically — over GitLab CI, Tekton, or GitHub Actions — is build-path muscle memory. The teams the lab will serve already know Jenkins, the operator wants to be able to use Jenkins job-config as part of the developer-readiness contract (ADR 0014), and the lab needs a CI executor that isn’t tied to a hosted SaaS for disconnected operation.

Decision

Deploy Jenkins as one standalone Ubuntu 24.04 cloud-init VM on br30.

PropertyValue
VM namejenkins-0
IPinternal-only (see connection-details/jenkins.md) — 30.30.30.x/16 in the lab service range
MACinternal-only
Private DNSjenkins-0.sub.comptech-lab.com, jenkins.sub.comptech-lab.com
Public edge DNSjenkins.apps.sub.comptech-lab.com
Public edge TLSexisting HAProxy wildcard certificate for *.apps.sub.comptech-lab.com
Jenkins package sourceupstream Jenkins LTS debian-stable APT repository
RuntimeOpenJDK 21
Jenkins homededicated data disk mounted at /var/lib/jenkins
Local-only admin credentialssecrets/jenkins/admin.env

Edge wiring

HAProxy terminates TLS on jenkins.apps.sub.comptech-lab.com and forwards HTTP to the Jenkins VM port 8080.

The Jenkins VM firewall:

  • allows SSH from the lab /16 network;
  • allows Jenkins port 8080 only from the HAProxy private address.

Direct Jenkins port access is intentionally not open to the whole lab network. Users reach Jenkins through HAProxy; operators reach Jenkins through SSH or the same edge route.

Alternatives considered

Run Jenkins on OpenShift (Helm chart or Jenkins Operator). Attractive because it would give Jenkins HA, OpenShift RBAC integration, and PVC-backed JENKINS_HOME. Rejected because:

  • Storage-light hubs (ADR 0004) push storage onto workload clusters, but the workload cluster’s ODF readiness state was not yet stable enough to depend on for CI.
  • A VM Jenkins is straightforward to back up (file-system snapshots of /var/lib/jenkins to MinIO).
  • The lab wants one fewer thing inside OpenShift for the first developer-readiness milestone.

GitLab CI as the only CI executor. Attractive because GitLab CI is already in the federated architecture. Rejected because:

  • The operator wants Jenkins for the historical-pattern reason above.
  • GitLab CI is also in scope — the build-path matrix in opp-full-plat/connection-details/build-path-matrix.md defines when each is used. Jenkins isn’t replacing GitLab CI; they cover different paths.
  • Having two independent build executors means Jenkins can be repaired by GitLab Runner when Jenkins itself is broken, per ADR 0015. One executor that depends only on itself for repair is a single point of failure.

Tekton Pipelines on OpenShift. Rejected because the storage-light hub policy removed Tekton from hub desired state (ADR 0004), and Tekton-on-spoke for general CI was not the operator’s preference. Tekton can still appear inside specific spoke namespaces for OpenShift-native build paths, but it is not the default CI executor.

Bypass HAProxy and put Jenkins on the public internet directly. Rejected because the lab does not want Jenkins exposed without a TLS-terminating reverse proxy that the lab controls, and HAProxy is already the lab’s edge TLS terminator for VM services.

Consequences

  • Jenkins is reachable at https://jenkins.apps.sub.comptech-lab.com/ via HAProxy + Let’s Encrypt wildcard. The same URL serves users and the Jenkins API.
  • Direct Jenkins port access is intentionally not open to the lab network. Everything that talks to Jenkins goes through HAProxy.
  • Jenkins credentials must never be printed, committed, copied to GitHub issues / wiki, or included in session reports. Local-only custody under secrets/jenkins/admin.env.
  • The first deployment is single-controller lab infrastructure, not a production CI dependency. Before Jenkins is treated as a production CI dependency, define:
    • backup / restore for JENKINS_HOME to MinIO with restore-drill evidence,
    • plugin governance (pinned versions, vulnerability scan),
    • job-as-code (JCasC + shared libraries, jobs in Git not in the UI),
    • Vault-backed credential handling (no plaintext credentials in Jenkins, fetch at job start from Vault),
    • upgrade rehearsal (LTS → next-LTS on a clone of the VM before doing it for real).
  • GitLab Runner is the independent fallback executor. Per ADR 0015, GitLab Runner can run Ansible/Terraform against the Jenkins VM to repair it; Jenkins is not the only thing that can repair Jenkins.

References

  • Source: opp-full-plat/adr/0009-jenkins-single-vm.md
  • Edge wiring rules: ADR 0005
  • Federation context: ADR 0015
  • Developer-readiness contract: opp-full-plat/adr/0014-developer-readiness-platform-contract.md
  • Jenkins operator notes: opp-full-plat/connection-details/jenkins.md, jenkins-ocp-path.md
  • Build-path matrix: opp-full-plat/connection-details/build-path-matrix.md

Last reviewed: 2026-05-11