How to have Jenkins dispatch builds for tags pushed to your multibranch pipeline-enabled Git repository

published Mar 19, 2026

A task so simple, and in principle possible, yet so hard to find documentation for.

How to have Jenkins dispatch builds for tags pushed to your multibranch pipeline-enabled Git repository

I spent a good deal of time today bashing my head against my desk because my Jenkins multibranch pipelines were discovering tags I pushed to them, but not building them.

After much Web searching, I found the problem: Jenkins, by default, won't build anything that isn't a branch, even if the Jenkins pipeline successfully discovers other non-branch heads.  Apparently, this is intended behavior!  You have to manually build tags, if you want to build a tag at all.

In my humble opinion — after hours of frustration — sigh.

The solution

First, install the basic branch build strategies plugin in your Jenkins setup.  Restart Jenkins.

Then, you have to add two branch strategies to your multibranch pipeline:

  1. A regular branches build strategy.
  2. A tags build strategy.

This is how they look in the user interface for editing the multibranch pipeline configuration:

If you're more the XML type for configuring Jenkins, add the boldened text from the following sample config.xml file to your multibranch pipeline configuration:

<?xml version='1.1' encoding='UTF-8'?>
<org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject plugin="workflow-multibranch@821.vc3b_4ea_780798">
  <actions/>
  <description>Job digital-audio-bridge.  Set up by generic build code.</description>
  <displayName>digital-audio-bridge</displayName>
  <properties/>
  <folderViews class="jenkins.branch.MultiBranchProjectViewHolder" plugin="branch-api@2.1280.v0d4e5b_b_460ef">
    <owner class="org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject" reference="../.."/>
  </folderViews>
  <healthMetrics>
    <com.cloudbees.hudson.plugins.folder.health.WorstChildHealthMetric plugin="cloudbees-folder@6.1079.vc0975c2de294">
      <nonRecursive>false</nonRecursive>
    </com.cloudbees.hudson.plugins.folder.health.WorstChildHealthMetric>
  </healthMetrics>
  <icon class="jenkins.branch.MetadataActionFolderIcon" plugin="branch-api@2.1280.v0d4e5b_b_460ef">
    <owner class="org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject" reference="../.."/>
  </icon>
  <orphanedItemStrategy class="com.cloudbees.hudson.plugins.folder.computed.DefaultOrphanedItemStrategy" plugin="cloudbees-folder@6.1079.vc0975c2de294">
    <pruneDeadBranches>true</pruneDeadBranches>
    <daysToKeep>-1</daysToKeep>
    <numToKeep>-1</numToKeep>
    <abortBuilds>false</abortBuilds>
  </orphanedItemStrategy>
  <triggers/>
  <disabled>false</disabled>
  <sources class="jenkins.branch.MultiBranchProject$BranchSourceList" plugin="branch-api@2.1280.v0d4e5b_b_460ef">
    <data>
      <jenkins.branch.BranchSource>
        <source class="jenkins.plugins.git.GitSCMSource" plugin="git@5.10.0">
          <id>digital-audio-bridge</id>
          <remote>file:///srv/git/digital-audio-bridge.git</remote>
          <credentialsId></credentialsId>
          <traits>
            <jenkins.plugins.git.traits.BranchDiscoveryTrait/>
            <jenkins.plugins.git.traits.TagDiscoveryTrait/>
          </traits>
        </source>
        <strategy class="jenkins.branch.DefaultBranchPropertyStrategy">
          <properties class="empty-list"/>
        </strategy>
        <buildStrategies>
          <jenkins.branch.buildstrategies.basic.BranchBuildStrategyImpl plugin="basic-branch-build-strategies@317.v85b_331d6cc42"/>
          <jenkins.branch.buildstrategies.basic.TagBuildStrategyImpl plugin="basic-branch-build-strategies@317.v85b_331d6cc42">
            <atLeastMillis>-1</atLeastMillis>
            <atMostMillis>604800000</atMostMillis>
          </jenkins.branch.buildstrategies.basic.TagBuildStrategyImpl>
        </buildStrategies>
      </jenkins.branch.BranchSource>
    </data>
    <owner class="org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject" reference="../.."/>
  </sources>
  <factory class="org.jenkinsci.plugins.workflow.multibranch.WorkflowBranchProjectFactory">
    <owner class="org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject" reference="../.."/>
    <scriptPath>Jenkinsfile</scriptPath>
  </factory>
</org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject>

You may be using a job DSL to define your multibranch pipelines.  Here is how my job DSL does it (again, the relevant code is boldened):

def defineJobViaDSL(job) {
    jobDsl(
        scriptText: """
    multibranchPipelineJob("${job}") {
        description "Job ${job}.  Set up by generic build code."
        displayName "${job}"
        branchSources {
          git {
            id = "${job}"
            remote("file:///srv/git/${job}.git")
          }
        }
        triggers {
          cron("H H * * *")
        }
        configure { x ->
          x / orphanedItemStrategy(class: 'com.cloudbees.hudson.plugins.folder.computed.DefaultOrphanedItemStrategy') {
            pruneDeadBranches('true')
            daysToKeep('-1')
            numToKeep('-1')
          }
          def traits = x / sources / data / 'jenkins.branch.BranchSource' / source / traits
          traits << 'jenkins.plugins.git.traits.BranchDiscoveryTrait' {}
          traits << 'jenkins.plugins.git.traits.TagDiscoveryTrait' {}
          def buildStrategies = x / sources / data / 'jenkins.branch.BranchSource' / buildStrategies
          buildStrategies << 'jenkins.branch.buildstrategies.basic.BranchBuildStrategyImpl' {}
          buildStrategies << 'jenkins.branch.buildstrategies.basic.TagBuildStrategyImpl' {
            atLeastMillis(-1)
            atMostMillis(604000000)
          }
        }
        if (!jenkins.model.Jenkins.instance.getItemByFullName("${job}")) {
          queue("${job}")
        }
    }
    """,
        sandbox: true
    )
}

Presto.  From now on, every time you push a tag to your multibranch pipeline — whether according to your SCM polling settings, or because you called the /job/<JOB>/build REST API endpoint — discovered tags will be built.