Jenkins multibranch configuration for branches and tags.png
How to have Jenkins dispatch builds for tags pushed to your multibranch pipeline-enabled Git repository
A task so simple, and in principle possible, yet so hard to find documentation for.
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:
- A regular branches build strategy.
- 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.