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

published Mar 19, 2026 , last modified Mar 22, 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

You can't change this behavior in the Jenkinsfile of your multibranch repository.  You have to make the change in the configuration of the project itself.

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.

Here's how you do this in three different ways:

Via the project configuration UI

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

Editing project config.xml

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>

Using JobDSL

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.