> ## Documentation Index
> Fetch the complete documentation index at: https://docs.controlplane.com/llms.txt
> Use this file to discover all available pages before exploring further.

# CPLN Jenkins

> Deploy Jenkins on Control Plane using the Template Catalog. Build agents are provisioned as Control Plane workloads on demand and deleted once idle. Covers the two prerequisite secrets, the API key permissions agents need, and why the agent GVC must have a single location.

Jenkins CI server with the [Control Plane cloud plugin](https://github.com/controlplane-com/cpln-jenkins-plugin) built in. Jenkins provisions each build agent as its own Control Plane workload and deletes it once idle, so build capacity costs nothing between builds.

The controller is configured entirely from code and boots straight to a login screen — there is no setup wizard to click through.

## Overview

### What Gets Created

* **Jenkins Controller** — a `stateful` workload serving HTTP on port 8080, pinned to a single replica.
* **Volume Set** — an `ext4` volume mounted at `/var/jenkins_home`, holding jobs, build history and installed plugins. A final snapshot is kept for 7 days when the volume set is deleted.
* **Cloud Configuration Secret** — created by the chart from your values and read by Jenkins at boot as Configuration as Code.
* **Identity and Policy** — grants the controller `reveal` on exactly the secrets it mounts, and nothing else.
* **Agent Workloads** — created and deleted by Jenkins itself at build time, not by the chart.

This template does not create a GVC. You must deploy it into an existing GVC.

## Prerequisites

**Two `opaque` secrets must exist before you install.**

<Warning>
  A missing prerequisite secret leaves the workload waiting on something that does not exist, and produces **zero log lines** — the container never starts, so there is nothing to log. See [Diagnosing a stuck install](#diagnosing-a-stuck-install).
</Warning>

<Steps>
  <Step title="Create the admin password secret">
    Anyone holding this password controls your CI, so use a strong one.

    ```bash theme={null}
    printf '%s' 'YOUR-STRONG-PASSWORD' | cpln secret create-opaque \
      --name my-jenkins-admin-password --encoding plain -f -
    ```

    Set `admin.passwordSecretName` to the name you used.
  </Step>

  <Step title="Create a Control Plane API key secret">
    Jenkins uses this key to create and delete agent workloads.

    ```bash theme={null}
    cpln serviceaccount add-key my-service-account --description jenkins
    ```

    Copy the `key` value from the output — it is shown once — then:

    ```bash theme={null}
    printf '%s' 'THE-KEY-VALUE' | cpln secret create-opaque \
      --name my-jenkins-cpln-api-key --encoding plain -f -
    ```

    Set `cloud.apiKeySecretName` to the name you used.
  </Step>

  <Step title="Grant the service account permissions">
    The key needs `view`, `create` and `delete` on workloads in the agent GVC, plus `view` on GVCs, identities and volume sets.

    A key with **no policy bindings authenticates successfully but provisions nothing** — this is the most common reason agents never appear.
  </Step>
</Steps>

<Warning>
  **The agent GVC must have exactly one location.** The plugin only accepts single-location GVCs, because each agent is a single replica and cannot be placed across locations.
</Warning>

Secret names are organization-wide, so give each Jenkins release its own secret names.

## Configuration

```yaml theme={null}
image: ghcr.io/controlplane-com/cpln-jenkins:2.0.1

admin:
  username: admin                                 # login name (not sensitive)
  passwordSecretName: my-jenkins-admin-password   # opaque secret; must EXIST BEFORE INSTALL

cloud:
  enabled: true
  apiKeySecretName: my-jenkins-cpln-api-key  # opaque secret; must EXIST BEFORE INSTALL
  gvc: ""                    # agent GVC; empty = the GVC Jenkins runs in. ONE location only
  agentWorkload: jenkins-agent  # name prefix for provisioned agent workloads
  agentImage: jenkins/inbound-agent:latest
  labels: cpln               # space-separated; jobs target these
  allowJobsWithoutLabels: true  # false = only jobs requesting a label above
  useUniqueAgents: true      # one dedicated agent per queued job
  executors: 1               # concurrent builds per agent
  cpu: 300                   # millicores per agent (minimum 50)
  memory: 512                # MiB per agent (minimum 128)
  retentionMins: 5           # delete an idle agent after this long
  provisioningCooldownSecs: 60  # gap between provisions; prevents over-scaling

resources:
  minCpu: 500m
  maxCpu: 1000m
  minMemory: 1Gi
  maxMemory: 2Gi

volumeset:
  capacity: 10               # GiB (minimum 10); holds JENKINS_HOME

publicAccess:
  enabled: false             # true publishes the Jenkins login to the internet

internalAccess:
  type: same-gvc             # none | same-gvc | same-org | workload-list
  workloads: []              # used with workload-list
```

### Admin Login

* `admin.username` — The login name. Not sensitive, so it stays a value.
* `admin.passwordSecretName` — Name of the `opaque` secret holding the password. It never passes through Helm values, so it does not land in the release.

<Note>
  The password is applied on **every** boot, not only the first. Rotating the secret and redeploying does change the login — unlike templates that seed an admin account once and ignore the value afterwards.
</Note>

### The Control Plane Cloud

These values pre-configure the **Control Plane cloud inside Jenkins** — the same settings you would otherwise fill in by hand under **Manage Jenkins → Clouds**. The chart renders them into a Configuration as Code file the controller reads at boot, so a fresh install already knows how to provision agents.

<Warning>
  Because that file is applied on **every** boot, editing these settings in the Jenkins UI does not survive a restart. Change them in values and upgrade instead.
</Warning>

* `cloud.gvc` — The GVC agents are provisioned into. Empty means the GVC Jenkins itself runs in.
* `cloud.labels` — Space-separated labels jobs use to target Control Plane agents.
* `cloud.useUniqueAgents` — On by default: one dedicated agent per queued job, so jobs do not wait behind each other.
* `cloud.retentionMins` — How long an idle agent survives before Jenkins deletes both the Jenkins node and the Control Plane workload.
* `cloud.cpu` / `cloud.memory` — Per-agent resources. The chart refuses to render below 50 millicores or 128 MiB, because the agent JVM fails to start or is OOM-killed below those floors.

The organization and the controller URL are derived at runtime from the platform's own values, so they cannot drift from where the workload actually runs.

### Access

`publicAccess` defaults to **off**, because the controller serves a login form and a CI server holds credentials for everything it deploys. A private controller is still reachable:

```bash theme={null}
cpln port-forward RELEASE_NAME-jenkins 8080:8080 --gvc GVC_NAME
```

Then open `http://localhost:8080`.

<Note>
  Access changes take up to a couple of minutes to propagate after toggling `publicAccess` or `internalAccess`. A measured toggle moved through `403` then `503` before reaching `200` over about 140 seconds.
</Note>

## Connecting

| What        | Where                                                                                                                                                                  |
| ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Public UI   | `https://<canonical-endpoint>` — only when `publicAccess.enabled` is true. Read it from `status.canonicalEndpoint` in `cpln workload get RELEASE_NAME-jenkins -o yaml` |
| Internal UI | `http://RELEASE_NAME-jenkins.GVC_NAME.cpln.local:8080`, subject to `internalAccess.type`                                                                               |
| Admin login | `admin.username` plus the payload of your `admin.passwordSecretName` secret                                                                                            |

<Warning>
  Use the fully-qualified `.cpln.local` name for internal access. The short workload name does not resolve reliably from every workload type.
</Warning>

## Running Builds on Control Plane Agents

Give a job the label from `cloud.labels`:

```groovy theme={null}
pipeline {
    agent { label 'cpln' }
    stages {
        stage('Build') {
            steps { sh 'echo building on a Control Plane agent' }
        }
    }
}
```

An agent workload appears in the GVC within about 30 seconds, runs the build, and is deleted once `cloud.retentionMins` elapses. With `allowJobsWithoutLabels: true` (the default), unlabelled jobs also run on Control Plane agents.

To route work away from Control Plane instead, set `allowJobsWithoutLabels: false` so only jobs explicitly requesting one of `cloud.labels` are picked up.

## Diagnosing a Stuck Install

A missing prerequisite secret produces **no log output at all**. The container never starts, so `cpln logs` returns zero lines and the deployment simply looks slow.

```bash theme={null}
cpln workload get-deployments RELEASE_NAME-jenkins --gvc GVC_NAME -o yaml
```

Read `status.versions[].message` — it names the missing secret.

<Warning>
  This is `get-deployments`, not `cpln workload get`. Plain `get` has no `versions` field and shows you nothing.
</Warning>

Creating the secret repairs the deployment on its own in roughly 5.5 to 10.5 minutes, or force a redeployment to skip the wait.

If Jenkins starts but no agents ever appear, the API key is the usual cause. The controller log shows `Failed to list workloads: 403` when the key lacks workload permissions — the Jenkins UI only shows an idle cloud, so the log is the place to look.

## Important Notes

* **Create both secrets before installing** — a missing one wedges the deployment silently.
* **The agent GVC must have exactly one location.** Multi-location GVCs are rejected by the plugin.
* **An API key with no policy bindings authenticates but provisions nothing.** It needs workload `view`, `create` and `delete` in the agent GVC.
* **`publicAccess.enabled: true` puts a Jenkins login on the internet.** Set a strong admin password first, or reach the UI through a port-forward tunnel instead.
* **Editing cloud settings in the Jenkins UI does not survive a restart** — Configuration as Code re-applies them at every boot.
* **Jobs, build history and installed plugins live on the volume set** and survive redeployment. Uninstalling the release deletes the volume set.

## External References

* [Control Plane Jenkins plugin](https://github.com/controlplane-com/cpln-jenkins-plugin)
* [Jenkins documentation](https://www.jenkins.io/doc/)
* [Jenkins Configuration as Code](https://www.jenkins.io/projects/jcasc/)
* [Jenkins pipeline syntax](https://www.jenkins.io/doc/book/pipeline/syntax/)
