How to Create a Jenkins Seed Job Using the Job DSL Plugin

Create a Jenkins seed job with the Job DSL plugin: a working folder and pipelineJob script, Script Security approval, the JCasC jobs: option, and checks.

In this guide we are going to learn how to create a Jenkins seed job.

A Jenkins seed job is a special job that is used to create other jobs. It is a normal Jenkins job that runs a Job DSL script; the script in turn contains instructions that create, update and delete additional jobs. Your job definitions live in Git as Groovy files, get reviewed like any other code, and a single build of the seed job brings Jenkins in line with them.

Everything below was tested on Jenkins 2.580.2 LTS (the jenkins/jenkins:lts-jdk21 image) with Job DSL plugin 3732.

Also check:

Prerequisites

  • A Jenkins server you can log in to as an administrator
  • git installed on the Jenkins controller (or on the agent that runs the seed job). The official Docker image already has it.
  • A Git repository to hold the DSL scripts

To install git on the Jenkins host:

# Rocky Linux / AlmaLinux / RHEL
sudo dnf -y install git

# Ubuntu / Debian
sudo apt install -y git

# openSUSE
sudo zypper in git

Install the plugins

The seed job needs these plugins:

  • job-dsl: runs the DSL scripts
  • git: checks out the repository holding the scripts
  • cloudbees-folder: for folder(...)
  • workflow-aggregator (Pipeline): for pipelineJob(...)

Install them from Manage Jenkins -> Plugins -> Available plugins, or, if you build your own Jenkins image, with jenkins-plugin-cli:

jenkins-plugin-cli --plugins job-dsl git cloudbees-folder workflow-aggregator

Add configuration-as-code too if you want to define the seed job itself in code (covered further down).

Create a credential for Git

If the repository is private, Jenkins needs a credential to clone it. On GitHub, create a fine-grained personal access token under Settings -> Developer settings -> Personal access tokens with read-only access to the repository’s Contents.

In Jenkins, go to Manage Jenkins -> Credentials -> System -> Global credentials -> Add Credentials. Choose Username with password, put your GitHub username in Username, the token in Password, and set the ID to github-token. The scripts below refer to the credential by that ID.

Write the DSL script

Create a repository (here https://github.com/example/jenkins-jobs.git) with the job definitions under jobs/:

jenkins-jobs/
└── jobs/
    └── backend.groovy

jobs/backend.groovy creates a folder with two Pipeline jobs in it: one that builds an application from its own Jenkinsfile, and one with an inline pipeline that is handy for checking that the seed job works.

folder('backend') {
  displayName('Backend')
  description('Jobs generated by the seed job. Do not edit by hand.')
}

pipelineJob('backend/api') {
  description('Builds the API from its Jenkinsfile')
  logRotator {
    numToKeep(30)
  }
  parameters {
    stringParam('BRANCH', 'main', 'Branch to build')
  }
  properties {
    pipelineTriggers {
      triggers {
        pollSCM {
          scmpoll_spec('H/5 * * * *')
        }
      }
    }
  }
  definition {
    cpsScm {
      scm {
        git {
          remote {
            url('https://github.com/example/api.git')
            credentials('github-token')
          }
          branch('*/${BRANCH}')
        }
      }
      scriptPath('Jenkinsfile')
      lightweight(true)
    }
  }
}

pipelineJob('backend/hello') {
  description('Inline pipeline, handy for testing the seed job')
  definition {
    cps {
      script('''
        pipeline {
          agent any
          stages {
            stage('Hello') {
              steps {
                echo 'Hello from a generated job'
              }
            }
          }
        }
      '''.stripIndent())
      sandbox(true)
    }
  }
}

A few notes:

  • Jobs inside a folder are named with a path: backend/api. The folder has to be defined (here, or already exist) before the jobs in it.
  • '*/${BRANCH}' is in single quotes on purpose: Groovy must not interpolate it. Jenkins expands the BRANCH parameter at build time.
  • Every option has to come from the plugins installed on your Jenkins. The full list, generated for your installation, is the API viewer at https://<your-jenkins>/plugin/job-dsl/api-viewer/index.html. Use it instead of copying old snippets from the internet: the DSL changes as plugins change.

Commit and push the file.

Create the seed job

With the plugins installed, set up the seed job:

  1. New Item -> enter seed-job -> Freestyle project -> OK.
  2. Under Source Code Management choose Git. Repository URL: https://github.com/example/jenkins-jobs.git, Credentials: github-token, Branch: */main.
  3. Under Build Steps -> Add build step -> Process Job DSLs. Choose Look on Filesystem and set DSL Scripts to jobs/*.groovy.
  4. Under Advanced, set Action for removed jobs and Action for removed views to Delete. The default, Ignore, leaves jobs behind in Jenkins after you remove them from the scripts.
  5. Save.

Optionally add a Build Triggers -> Poll SCM schedule (or a webhook) so the seed job runs whenever the DSL repository changes.

Approve the script (Script Security)

Run Build Now on the seed job. The first build on any Jenkins with security enabled fails:

Processing DSL script jobs/backend.groovy
ERROR: script not yet approved for use
Finished: FAILURE

This is the Script Security plugin doing its job. A DSL script runs with full access to Jenkins, so an administrator has to approve it. You have three options:

  1. Approve it. Go to Manage Jenkins -> In-process Script Approval (/manage/scriptApproval/), review the pending script and click Approve. The approval is tied to the script’s hash, so every change to the file needs a new approval. Fine for a script that rarely changes, tedious otherwise.

  2. Run the DSL in the Groovy sandbox. Tick Use Groovy Sandbox in the Process Job DSLs step. The sandbox only allows whitelisted calls, and the seed job must run as a specific user. Without the Authorize Project plugin configured, the build fails with:

    ERROR: You must configure the DSL job to run as a specific user in order to use the Groovy sandbox.
    
  3. Turn off Script Security for Job DSL. Manage Jenkins -> Security -> untick Enable script security for Job DSL scripts. Only do this when the DSL repository is as trusted as Jenkins itself (only admins can merge to it), because anyone who can change those scripts effectively has admin on Jenkins.

Verify the generated jobs

Build the seed job again. The console log lists what it created:

Processing DSL script jobs/backend.groovy
Added items:
    GeneratedJob{name='backend/api'}
    GeneratedJob{name='backend'}
    GeneratedJob{name='backend/hello'}
Finished: SUCCESS

The Backend folder now appears on the Jenkins dashboard with both jobs in it. Each generated job’s page also shows a link back to the seed job that created it, and the seed job’s page lists its generated items.

You can check from the command line too, with an API token (your user -> Security -> API Token):

curl -s -u admin:$JENKINS_TOKEN \
  'https://jenkins.example.com/api/json?tree=jobs%5Bname,jobs%5Bname%5D%5D'
{"_class":"hudson.model.Hudson","jobs":[{"_class":"com.cloudbees.hudson.plugins.folder.Folder","name":"backend","jobs":[{"_class":"org.jenkinsci.plugins.workflow.job.WorkflowJob","name":"api"},{"_class":"org.jenkinsci.plugins.workflow.job.WorkflowJob","name":"hello"}]},{"_class":"hudson.model.FreeStyleProject","name":"seed-job"}]}

Run backend/hello and its console ends with:

Hello from a generated job
Finished: SUCCESS

When you later delete a job from the scripts, the next seed build reports it and, with the removal action set to Delete, removes it from Jenkins:

Unreferenced items:
    GeneratedJob{name='hello'}
Removed items:
    GeneratedJob{name='hello'}

Do not edit generated jobs in the UI: the next seed build overwrites the change.

Alternative: define the seed job with JCasC

Clicking the seed job together in the UI is the one manual step left. With the Configuration as Code plugin, a jobs: block in the JCasC YAML runs Job DSL when Jenkins starts, so the seed job is itself code:

jobs:
  - script: |
      job('seed-job') {
        description('Generates all other jobs from jobs/*.groovy')
        scm {
          git {
            remote {
              url('https://github.com/example/jenkins-jobs.git')
              credentials('github-token')
            }
            branch('*/main')
          }
        }
        triggers {
          scm('H/5 * * * *')
        }
        steps {
          dsl {
            external('jobs/*.groovy')
            removeAction('DELETE')
            removeViewAction('DELETE')
          }
        }
      }

Point Jenkins at the file with the CASC_JENKINS_CONFIG environment variable (for example CASC_JENKINS_CONFIG=/var/jenkins_home/casc.yaml). The seed job is created at startup, and re-created whenever you apply the configuration again from Manage Jenkins -> Configuration as Code -> Reload existing configuration. Scripts under jobs: are trusted configuration, so they do not need script approval; the seed job’s own jobs/*.groovy still goes through Script Security as described above.

If you choose option 3 above, JCasC can set it too:

security:
  globalJobDslSecurityConfiguration:
    useScriptSecurity: false

For a handful of jobs you could skip the seed job entirely and put every job in jobs:. Keeping a seed job is better once the list grows: changing a job is then a commit to the DSL repository and a seed build, without touching the Jenkins configuration or restarting.

Troubleshooting

  • ERROR: script not yet approved for use: approve the script, or see the options under Script Security above. It comes back after every change to an approved script.
  • No signature of method: ... or No such DSL method ...: a typo, or a method from a plugin that is not installed on this Jenkins. The error names the file and line and usually suggests close matches (Possible solutions: multibranchPipelineJob(java.lang.String)). Check the method in your instance’s API viewer.
  • Processing DSL script never appears / no files found: the DSL Scripts pattern is relative to the workspace root. Check the checkout in the seed job’s workspace and the path jobs/*.groovy.
  • Generated jobs are not removed: Action for removed jobs is still Ignore. Set it to Delete (or Disable).
  • Clone fails with an authentication error: the github-token credential is missing, has the wrong ID, or the token lacks access to the repository.

Conclusion

In this guide we set up a seed job in Jenkins that creates, updates and removes other jobs from Groovy files in Git, dealt with Script Security, and defined the seed job itself with JCasC so a new Jenkins can rebuild all of its jobs from code.

Last updated on Nov 10, 2026 12:49 +0300
comments powered by Disqus
Citizix Ltd
Built with Hugo
Theme Stack designed by Jimmy