Jenkins powers CI/CD pipelines for millions of developers worldwide, but managing Jenkins configuration through the web UI doesn’t scale. This guide compares the three leading approaches to Jenkins configuration-as-code: Jenkins Configuration-as-Code (JCasC), Job DSL Plugin, and Shared Pipeline Libraries.

Why Manage Jenkins as Code?

Manual Jenkins configuration through the web UI creates several problems at scale:

  • Configuration drift — changes made by different administrators are not tracked or version-controlled
  • Disaster recovery — rebuilding a Jenkins master from scratch requires manual recreation of every setting
  • Compliance requirements — regulated industries need auditable configuration changes with approval workflows
  • Multi-instance management — keeping multiple Jenkins instances (dev, staging, prod) in sync is error-prone
  • Team collaboration — code review and pull request workflows improve configuration quality

Configuration-as-code solves these problems by storing all Jenkins settings in version-controlled files that can be reviewed, tested, and deployed like application code.

Jenkins Configuration-as-Code Approaches

ApproachScopeLanguageVersion ControlLearning Curve
JCasCJenkins master configurationYAMLYes (YAML files)Low
Job DSLJob/pipeline definitionsGroovyYes (DSL scripts)Medium
Pipeline LibrariesPipeline logicGroovyYes (Git repos)Medium

Jenkins Configuration-as-Code (JCasC)

JCasC (Configuration-as-Code plugin) manages the entire Jenkins master configuration through YAML files. It covers security realms, authorization, plugin settings, credentials, and system configuration.

Architecture

1
2
3
4
5
YAML Config Files -> JCasC Plugin -> Jenkins Master Configuration
     |                    |                  |
  casc.yaml          Configuration     Security, Plugins,
  security.yaml      Applied on        Credentials, Nodes,
  plugins.yaml       Startup/Reload    System Settings

Docker Compose Deployment

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
services:
  jenkins:
    image: jenkins/jenkins:lts-jdk17
    ports:
      - "8080:8080"
      - "50000:50000"
    volumes:
      - jenkins_home:/var/jenkins_home
      - ./jcasc:/var/jenkins_config:ro
    environment:
      - CASC_JENKINS_CONFIG=/var/jenkins_config
      - JENKINS_OPTS="--httpPort=8080"
    restart: unless-stopped

volumes:
  jenkins_home:

JCasC Configuration Example

Create casc.yaml in your configuration directory:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
jenkins:
  systemMessage: "OpenSwap Guide CI/CD Platform"
  numExecutors: 4
  mode: NORMAL
  securityRealm:
    ldap:
      configurations:
        - server: "ldap://ldap.example.com:389"
          rootDN: "dc=example,dc=com"
          userSearch: "uid={0}"
          groupSearchBase: "ou=groups"
          managerDN: "cn=admin,dc=example,dc=com"
          managerPasswordSecret: "${LDAP_PASSWORD}"

  authorizationStrategy:
    roleBased:
      roles:
        global:
          - name: "admin"
            description: "Jenkins administrators"
            permissions:
              - "Overall/Administer"
            entries:
              - user: "admin"
          - name: "developer"
            description: "Developers"
            permissions:
              - "Job/Build"
              - "Job/Read"
            entries:
              - group: "developers"

  nodes:
    - permanent:
        name: "build-agent-01"
        remoteFS: "/home/jenkins"
        launcher:
          ssh:
            host: "build-agent-01.example.com"
            credentialsId: "ssh-agent-key"

  tool:
    git:
      installations:
        - name: "Default"
          home: "/usr/bin/git"
    maven:
      installations:
        - name: "Maven 3.9"
          properties:
            - installSource:
                installers:
                  - maven:
                      id: "3.9.6"

Managing Plugins via JCasC

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
plugins:
  - id: "configuration-as-code"
    source:
      latest: true
  - id: "ldap"
    source:
      latest: true
  - id: "role-strategy"
    source:
      latest: true

Reloading Configuration

Apply configuration changes without restarting Jenkins:

1
2
curl -X POST "http://jenkins:8080/configuration-as-code/reload" \
  -u "admin:admin-password"

Key Features

  • Full master configuration — security, authorization, plugins, credentials, nodes, system settings
  • YAML-based — human-readable, version-control friendly configuration format
  • Environment variable substitution — use ${ENV_VAR} for secrets and environment-specific values
  • Configuration export — export current Jenkins configuration as YAML with /configuration-as-code/export
  • Validation — validate YAML syntax before applying with /configuration-as-code/check
  • Merge support — split configuration across multiple YAML files for organization

Strengths

  • Complete coverage — manages every aspect of Jenkins master configuration
  • GitOps-friendly — configuration files stored in Git with full audit trail
  • Disaster recovery — rebuild Jenkins master from configuration files in minutes
  • Multi-instance sync — deploy identical configuration across Jenkins instances
  • Active community — widely adopted with extensive documentation and examples

Limitations

  • Does not manage jobs — JCasC configures the master, not individual jobs or pipelines
  • Plugin dependency — some plugins have incomplete JCasC support
  • YAML complexity — large configurations become difficult to maintain in a single file
  • No job logic — does not replace pipeline definitions or job configurations

Job DSL Plugin

The Job DSL Plugin generates Jenkins jobs programmatically using Groovy scripts. It is the most established approach to Jenkins configuration-as-code for job definitions.

Architecture

1
2
3
4
5
Groovy DSL Scripts -> Job DSL Plugin -> Jenkins Jobs (auto-generated)
     |                      |                  |
  jobs.dsl.groovy       DSL Interpreter    Freestyle, Pipeline,
  seed-job.groovy         Processes         Multi-branch Jobs
                          Scripts

Seed Job Configuration

Create a seed job that processes DSL scripts:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
job('seed-job') {
    scm {
        git {
            remote {
                url('https://github.com/openswap/jenkins-dsl.git')
                branch('main')
            }
        }
    }
    triggers {
        scm('H/5 * * * *')
    }
    steps {
        dsl {
            external('jobs/**/*.groovy')
            ignoreExisting(false)
            removeAction('DELETE')
        }
    }
}

Job DSL Script Examples

Define a pipeline job:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
pipelineJob('build-and-deploy') {
    definition {
        cps {
            sandbox(true)
            script(readFileFromWorkspace('Jenkinsfile'))
        }
    }
    parameters {
        stringParam('ENVIRONMENT', 'staging', 'Target environment')
        choiceParam('BRANCH', ['main', 'develop'], 'Git branch')
    }
    logRotator {
        numToKeep(20)
    }
}

Define a multi-branch pipeline:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
multibranchPipelineJob('microservice-builds') {
    branchSources {
        git {
            id('microservice-repo')
            remote('https://github.com/openswap/microservices.git')
            credentialsId('github-token')
        }
    }
    orphanedItemStrategy {
        discardOldItems {
            numToKeep(5)
        }
    }
}

Key Features

  • Programmatic job creation — generate hundreds of jobs from a single DSL script
  • Conditional logic — use Groovy loops, conditions, and functions for dynamic job generation
  • Template patterns — define job templates and instantiate with different parameters
  • View/folder management — organize jobs into views and folders programmatically
  • Seed job pattern — a single seed job processes all DSL scripts on schedule
  • Remove orphaned jobs — automatically delete jobs no longer defined in DSL

Strengths

  • Mature and stable — over 10 years of development with extensive plugin ecosystem
  • Groovy flexibility — full programming language for complex job generation logic
  • Template reuse — define job patterns and instantiate across teams/projects
  • Self-documenting — DSL scripts serve as documentation for job configurations

Limitations

  • Groovy learning curve — requires Groovy programming knowledge
  • Does not manage Jenkins master — only creates jobs, not security or system configuration
  • DSL versioning — DSL syntax changes between plugin versions may require script updates
  • Debugging complexity — errors in DSL scripts can be difficult to diagnose

Shared Pipeline Libraries

Jenkins Shared Libraries enable teams to define reusable pipeline logic in Git repositories, promoting code reuse and standardization across CI/CD pipelines.

Architecture

1
2
3
4
5
Git Repository -> Jenkins Shared Library -> Pipeline Steps/Functions
     |                      |                  |
  vars/                 @Library          Reusable Steps:
  src/                  Annotation        build(), test(),
  resources/            Import            deploy(), notify()

Repository Structure

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
shared-library/
+-- vars/
|   +-- build.groovy
|   +-- test.groovy
|   +-- deploy.groovy
|   +-- notify.groovy
+-- src/
|   +-- org/openswap/
|       +-- Utils.groovy
+-- resources/
|   +-- templates/
|       +-- deployment.yaml
+-- Jenkinsfile

Library Step Definition

vars/build.groovy:

1
2
3
4
5
6
7
def call(String platform = 'linux') {
    stage('Build') {
        echo "Building for platform: ${platform}"
        sh "docker build -t ${IMAGE_NAME}:${BUILD_NUMBER} ."
    }
}
return this

vars/deploy.groovy:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
def call(String environment, String version) {
    stage("Deploy to ${environment}") {
        if (environment == 'production') {
            input message: "Approve production deployment?", ok: "Deploy"
        }
        sh "kubectl set image deployment/app app=${IMAGE_NAME}:${version} -n ${environment}"
        sh "kubectl rollout status deployment/app -n ${environment}"
        notify('success', "Deployed ${version} to ${environment}")
    }
}
return this

Using the Library in Pipelines

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
@Library('openswap-shared') _

pipeline {
    agent any
    parameters {
        choice(name: 'ENVIRONMENT', choices: ['staging', 'production'])
    }
    stages {
        stage('Build') {
            steps { build('linux') }
        }
        stage('Test') {
            steps { test(['unit', 'integration']) }
        }
        stage('Deploy') {
            steps { deploy(params.ENVIRONMENT, params.VERSION) }
        }
    }
}

Configure Library in Jenkins via JCasC

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
unclassified:
  globalLibraries:
    libraries:
      - name: "openswap-shared"
        defaultVersion: "main"
        implicit: false
        allowVersionOverride: true
        retriever:
          modernSCM:
            scm:
              git:
                remote: "https://github.com/openswap/shared-library.git"
                credentialsId: "github-token"

Key Features

  • Reusable pipeline steps — define once, use across all pipelines
  • Versioned libraries — pin pipelines to specific library versions
  • Testing support — test library code with JenkinsPipelineUnit framework
  • Resource files — include configuration templates, scripts, and assets
  • Class libraries — define Groovy classes for complex logic in src/
  • Multiple libraries — import multiple shared libraries in a single pipeline

Strengths

  • Code reuse — eliminate duplicate pipeline logic across teams
  • Standardization — enforce consistent build, test, and deploy patterns
  • Centralized updates — fix a bug or add a feature in one place, all pipelines benefit
  • Version control — library code stored in Git with full history and review
  • Testing framework — JenkinsPipelineUnit enables unit testing of pipeline steps

Limitations

  • Pipeline logic only — does not manage Jenkins master configuration or job definitions
  • Groovy dependency — requires Groovy programming knowledge
  • Library management — coordinating library updates across teams requires planning
  • Debugging — errors in shared library code can be difficult to trace to specific pipelines

Comparison Summary

FeatureJCasCJob DSLPipeline Libraries
ScopeJenkins masterJob definitionsPipeline logic
FormatYAMLGroovy DSLGroovy
Security configYesNoNo
Plugin configYesNoNo
Job creationNoYesNo
Pipeline logicNoEmbedsYes
GitOps readyYesYesYes
Version pinningVia GitVia GitVia Git tag/branch
Testing supportYAML validationLimitedJenkinsPipelineUnit
Learning curveLow (YAML)Medium (Groovy)Medium (Groovy)

Combining All Three Approaches

The most robust Jenkins-as-code setup uses all three tools together:

  1. JCasC configures the Jenkins master (security, plugins, credentials, agents)
  2. Job DSL generates job definitions from seed jobs
  3. Shared Libraries provide reusable pipeline logic for all jobs
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
Git Repository
+-- jcasc/
|   +-- casc.yaml
|   +-- security.yaml
+-- dsl/
|   +-- seed-job.groovy
|   +-- jobs/
|       +-- build-pipeline.groovy
+-- shared-library/
    +-- vars/
    +-- src/

Deploy pipeline:

1
2
3
4
5
6
7
# 1. Apply JCasC configuration
curl -X POST "http://jenkins/configuration-as-code/reload" -u "admin:token"

# 2. Run seed job to generate jobs
curl -X POST "http://jenkins/job/seed-job/build" -u "admin:token"

# 3. Shared library already loaded via JCasC globalLibraries config

Why Self-Host Jenkins Configuration?

Managing Jenkins through the web UI creates operational risks that grow exponentially with team size. Configuration-as-code enables version control, code review, and automated deployment of Jenkins settings — the same practices applied to application code. Self-hosted Jenkins with configuration-as-code is essential for teams in regulated industries requiring audit trails for CI/CD infrastructure changes.

For teams evaluating the broader CI/CD ecosystem, our ArgoCD vs Flux GitOps comparison covers GitOps platforms, and our Argo Rollouts vs Flagger progressive delivery guide covers deployment strategies.

FAQ

What is the difference between JCasC and Job DSL?

JCasC (Configuration-as-Code) manages Jenkins master configuration — security, plugins, credentials, and system settings — using YAML files. Job DSL generates Jenkins jobs using Groovy scripts. They serve different purposes: JCasC configures the infrastructure, Job DSL creates the workloads. Most teams use both together.

Can JCasC manage Jenkins jobs?

No, JCasC manages Jenkins master configuration only. It cannot create or manage individual jobs, pipelines, or build configurations. Use Job DSL or Multi-branch Pipeline jobs for job definitions. JCasC can configure the Shared Library that jobs reference.

How do I version-control Jenkins credentials with JCasC?

Never store credentials directly in JCasC YAML files. Use environment variable substitution: ${SECRET_VAR} in your YAML, then inject the actual value through Kubernetes Secrets, HashiCorp Vault, or Jenkins credential store. JCasC supports credentialsId references to Jenkins-stored secrets.

How do I test Job DSL scripts before deploying?

Use the Job DSL Playground (available at /plugin/job-dsl/api-viewer) to test DSL syntax. For more thorough testing, run DSL scripts in a staging Jenkins instance before applying to production. The Job DSL plugin also supports a “sandbox” mode that previews generated jobs without creating them.

How do I migrate from manual Jenkins configuration to JCasC?

  1. Install the JCasC plugin
  2. Export current configuration: visit /configuration-as-code/export to generate YAML
  3. Review and clean the exported YAML (remove sensitive data, simplify)
  4. Store YAML in Git repository
  5. Configure Jenkins to load JCasC from the Git repository
  6. Test by reloading configuration and verifying all settings are applied

Can Shared Pipeline Libraries replace Jenkins plugins?

Shared Libraries complement plugins, not replace them. Plugins provide Jenkins core functionality (SCM integration, artifact management, notifications). Shared Libraries organize pipeline logic into reusable components. Many pipeline steps in shared libraries call plugin-provided functionality (e.g., sh steps use the Shell plugin, docker.build uses the Docker plugin).