Jenkins Configuration as Code, YAML Bundles, Secrets, Reloading, Validation, and Git-Managed Controller State: Concepts, Architecture, and Mental Model
Jenkins Configuration as Code (JCasC) turns controller settings into reviewable YAML, but reproducibility only exists when plugin prerequisites, secret sources, reload behavior, Git identity, live state and recovery artifacts remain separate and observable. Build that mental model before changing the controller.
Learning objectives
- Explain what JCasC owns and what remains outside it.
- Trace versioned YAML and secret references through configurator validation into live controller state.
- Separate plugin binaries, JENKINS_HOME, Git source, secrets and exported evidence.
- Use read-only inspection before a reload or restart.
- Define a drift-and-recovery model that makes controller configuration reproducible.
1. The practical problem: UI configuration is easy to change and hard to reproduce
A Jenkins controller accumulates global settings: security realm, authorization, node defaults, clouds, tool configuration, credentials providers, system messages and plugin-specific settings. If those settings exist only as clicks in the UI, a second controller or disaster-recovery rebuild depends on memory, screenshots and undocumented ordering.
JCasC moves supported controller configuration into human-readable YAML that can be code-reviewed. That does not make all Jenkins state declarative. Plugins still have to be installed separately; jobs/build records remain separate; secrets require a protected source; some plugin data is not portable; and a running controller can drift after manual UI edits until configuration is reapplied.
2. Mental model: source → schema → apply → evidence → recovery
The causal chain is: versioned JCasC YAML plus secret references → installed plugin/configurator schema → validation → controller startup/reload → live runtime settings → export/drift evidence → Git rollback and, when needed, a JENKINS_HOME snapshot.
flowchart TD A[Git commit: JCasC YAML] --> B[Secret references] B --> C[Installed plugins + configurator schema] C --> D[Parse / resolve / validate] D --> E[Startup or administrative reload] E --> F[Live controller configuration] F --> G[Export + read-only inspection] G --> H[Drift comparison] H --> I[Revert commit / restore snapshot] I --> D
YAML → schema: a key is meaningful only if Jenkins core or an installed plugin contributes the matching configurator. Secret reference → resolution: the placeholder is committed, but the value comes from a protected source at apply time. Validation → apply: YAML syntax success is not enough; JCasC must resolve configurators and types. Apply → live state: reload changes controller-global state without recreating builds/workspaces. Live state → evidence: export, logs and UI inspection are observations, not automatically the canonical source. Recovery: Git handles desired configuration; snapshots still protect controller data that JCasC does not own.
3. State that must remain distinct
| Layer | Examples | Evidence |
|---|---|---|
| Controller/runtime | Jenkins 2.568.3, Java 21, JENKINS_HOME | startup log, About/System Information, image digest |
| Plugin/configurator | JCasC 2121…, Credentials 1511…, plugin-specific schemas | Installed plugins + JCasC reference |
| Desired configuration | YAML files and CASC_JENKINS_CONFIG |
Git commit, file SHA-256, source path |
| Secret source | file/Docker/Kubernetes/Vault/env resolver | source type/path/owner; never the secret value |
| Live configuration | system message, security realm, global settings | UI/API/export after apply |
| Job/build | item XML, build number, Pipeline CPS state | job/build URL, source SHA, run logs |
| Agent/workspace | node process and workspace files | node/label/path/tool evidence |
| Recovery | accepted Git commit plus controller snapshot | restore drill and verification record |
Changing a JCasC system message does not alter a running agent workspace. Installing a new plugin is not a JCasC reload. Restoring YAML cannot recreate lost build history. Keep those boundaries explicit.
4. How JCasC finds configuration
The plugin reads CASC_JENKINS_CONFIG or the equivalent
Java system property. A source may be a local file, directory, file
URI, HTTP/HTTPS URL, or comma-separated set. If the source is a
directory, JCasC recursively discovers YAML files.
ConfiguratorException. Do not design “base.yaml +
override.yaml” around traversal order.
5. Plugin installation is a prerequisite, not JCasC content
JCasC configures objects exposed by Jenkins core and installed plugins. It does not install those plugins. A reproducible controller therefore needs two versioned inputs: a plugin manifest/controller image and a JCasC repository. Upgrade the plugin set in a tested candidate first, validate the candidate schema against the YAML, then promote the pair.
If YAML refers to a plugin setting that is missing or renamed, the failure belongs to the controller/plugin/configurator layer—not the queue or agent layer.
6. Secret placeholders and strict resolution
JCasC supports SecretSource resolution. The source code should
contain a placeholder such as ${lab_admin_password},
while the value is injected from a file-backed secret, external
provider or other protected resolver. Environment variables can also
resolve values, but they are broadly observable in many runtimes and
are not the preferred home for long-lived secrets.
Enable CASC_STRICT_SECRET_RESOLUTION=true for governed
environments. Without strict resolution, an unresolved variable can
become an empty string with a warning; strict mode aborts the reload
when a required secret is missing, leaving the prior working
configuration in place.
7. Read-only inspection before mutation
Before any reload, record the controller and the exact source identity. Do not dump the whole container environment after secrets are injected.
# Safe host-side inspection of non-secret identities only
sha256sum jcasc/*.yaml
git rev-parse HEAD
docker image inspect jenkins/jenkins:2.568.3-lts-jdk21 \
--format '{{json .RepoDigests}}'
# On the controller UI:
# Manage Jenkins → Plugins → Installed
# Manage Jenkins → Configuration as Code
# Record JCasC plugin version, configured source, and last-load evidence.
Also inspect the instance-specific JCasC reference/documentation. That reference is generated from the installed controller/plugin set, which is exactly why a YAML file must be tested against the target baseline rather than an abstract example.
8. Validation has layers
| Gate | What it catches | What it cannot prove |
|---|---|---|
| YAML parser/linter | indentation and syntax | Jenkins configurator names/types |
| Instance reference/schema | available configurators and many types | all runtime side effects; JSON schema is still beta |
| JCasC check/apply on clone | source conflicts, secret resolution, plugin model | every downstream job/external integration |
| Clean restart test | startup initialization and plugin/config coherence | production traffic behavior |
| Representative smoke tests | critical Jenkins paths after change | unknown/unexercised workflows |
9. Reload is an administrative state change; restart is a runtime boundary
Current JCasC supports controlled reload from the UI, authenticated endpoint and Jenkins CLI. The reload action is privileged because it can change global security and controller behavior. Do not expose a reload token casually or call reload from a normal Pipeline; upstream documentation specifically discourages the Groovy/Pipeline shortcut.
Reload is appropriate when the installed configurators support the desired change and no plugin/core/Java lifecycle change is involved. Restart is the stronger boundary when plugins or runtime changed, when startup itself must be tested, or when the rollback plan depends on a clean initialization.
10. Export, drift and Git identity
The export feature is valuable evidence: it shows what the current controller can serialize. It is not automatically a clean replacement for the authored repository. Exports may include defaults, plugin-version-specific structure and encrypted values bound to the current controller secret key. Review and normalize rather than committing exports blindly.
Drift is the difference between approved source and live controller state. A manual UI edit to a JCasC-owned field can create temporary drift; a controlled reload/startup should reconcile it. Record both the drift observation and the Git commit that reasserted state.
11. Common wrong approaches
- Commit literal passwords/tokens into YAML because “the repository is private.”
- Use a mutable remote YAML URL with no commit/digest evidence.
- Assume JCasC installs missing plugins.
- Split the same attribute across files and expect last-file-wins.
- Treat “reload returned 200” as proof every job and external integration still works.
- Delete JENKINS_HOME when a YAML key fails instead of preserving the first error.
- Allow application developers to edit admin-equivalent configuration without protected review.
Knowledge check
Answer before revealing the explanation.
1. Why does JCasC not replace plugin management?
JCasC configures Jenkins core and installed plugin configurators. It does not install the plugin binaries that define those configurators, so the reviewed plugin set must exist before YAML that references it can be applied.
2. What does CASC_JENKINS_CONFIG identify?
It identifies one or more JCasC sources: a file, a folder, a file URI, an HTTP/HTTPS URL, or a comma-separated set of such sources. Folder sources are recursively scanned for YAML files.
3. Why must multiple JCasC files be supplementary?
JCasC rejects conflicting assignments rather than using file traversal order as precedence. Two fragments that both configure the same attribute can raise a ConfiguratorException.
4. Why is a JCasC export evidence rather than automatically the canonical source file?
Export reflects what the running controller/configurators can serialize at that moment. It may be verbose, plugin-version dependent, contain instance-bound encrypted values, and is not guaranteed to be the minimal portable source you should commit.
5. What does strict secret resolution change?
With CASC_STRICT_SECRET_RESOLUTION=true, an unresolved secret without a default aborts the configuration reload, preserving the previous working configuration instead of replacing the missing value with an empty string.
Official references and version notes
- Jenkins — Configuration as Code — handbook overview and operating model.
-
Configuration as Code plugin
— current release, installation,
CASC_JENKINS_CONFIG, supplementary-source rules and plugin prerequisites. - JCasC upstream repository — canonical feature documentation, examples and compatibility notes.
- JCasC — Handling Secrets — SecretSource behavior, file/Docker/Kubernetes/Vault options and strict secret resolution.
- JCasC — Triggering Configuration Reload — UI, authenticated API and CLI reload paths and their privilege requirements.
- JCasC — JSON Schema — instance-specific schema support; current implementation remains beta and should not be the only validation gate.
- Credentials plugin — current credentials API baseline used by the disposable controller image.
- Jenkins LTS changelog and Java support policy — current core/runtime assumptions.
2.568.3 LTS with Java 21; Jenkins
2.568.3 is tested with Java 21 and 25. Configuration as Code is
2121.v86fe99d4b_b_a_b_, released 30 August 2026 and
requiring Jenkins 2.541.1. Credentials is
1511.v2e3cb_0008ef0, also requiring Jenkins 2.541.1.
The disposable image starts from
jenkins/jenkins:2.568.3-lts-jdk21; record its
platform-specific image digest before execution. Re-check versions,
security advisories and configurator reference output before reusing
the lab because plugin schemas can change independently of Jenkins
core.
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this
address.