How do agent labels route a Jenkins build to the right machine, and what happens to a build whose `agent { label 'gpu && linux' }` expression matches no online agent?
answer
- capability, not hostname
- boolean expression over node tags
- case-sensitive strings
- no match does not mean failure
- clouds can invent the capacity on demand
basics
~20 sLabels are tags assigned to Jenkins nodes; a pipeline's label expression, which supports && || ! and parentheses, selects nodes that satisfy it. If none matches, the build sits in the queue indefinitely rather than failing.
solid answer
~60 sEvery Jenkins node carries a set of labels: its own name as an implicit self-label, plus whatever you assign in the node configuration — `linux`, `docker`, `gpu`, `jdk17`. A pipeline requests capability rather than a machine: `agent { label 'gpu && linux' }` in declarative, `node('gpu && linux') { }` in scripted. The expression language supports `&&`, `||`, `!`, and parentheses, so you can write `linux && !legacy`. Jenkins picks any node satisfying the expression that has a free executor and is not restricted from running the job. The failure mode surprises people: if nothing matches, the build does not fail. It queues, and the queue item shows a reason such as there being no node with that label. It will wait forever unless something changes. Two things can change it — an agent with those labels comes online, or a configured cloud (Kubernetes, EC2, Docker) has a template advertising that label and provisions a new agent on demand. Guard against silent waits with `options { timeout(...) }` and queue-length monitoring.
code
groovy · 14 linespipeline {
agent none
options { timeout(time: 1, unit: 'HOURS') }
stages {
stage('Unit tests') {
agent { label 'linux && jdk17' }
steps { sh './gradlew test' }
}
stage('GPU benchmark') {
agent { label 'linux && gpu && !deprecated' }
steps { sh './bench.sh' }
}
}
}go deeper
Know that nodes carry labels, that agent { label 'linux' } asks for a node with that label, and that a node can carry several labels at once.
Explain the expression operators &&, ||, ! and grouping, that labels are case-sensitive, and — the part most candidates miss — that an unmatched expression leaves the build queued rather than failed.
Talk about operating it: diagnosing a stuck queue item from its blocked reason, adding a pipeline timeout so stuck builds surface, and migrating a fleet by adding new labels before retiring old ones.
Own the taxonomy as a contract between pipelines and infrastructure. Decide what labels mean, keep the vocabulary small, and plan how a hardware or platform change reaches hundreds of Jenkinsfiles without stranding them.
## Labels are capabilities, not hostnames A Jenkins agent has a name (`build-node-07`) and a label string configured on the node. Jenkins treats the node's own name as an implicit label too, so `node('build-node-07')` pins to that exact machine. Almost always you should not do that. Pinning to a hostname makes the pipeline break when the machine is replaced, and it prevents Jenkins from balancing work. Labels exist so a Jenkinsfile can say what it *needs* — `linux`, `docker`, `windows`, `jdk17`, `gpu`, `arm64` — and let the scheduler find something that has it. A node's label field is a space-separated list: ``` linux docker jdk17 build ``` That node satisfies `linux`, `docker`, `linux && docker`, `docker || windows`, and `linux && !windows`. ## The expression language Label expressions support boolean operators: `&&` (and), `||` (or), `!` (not), parentheses for grouping, and quoting for labels containing spaces or special characters. Jenkins also supports `->` (implies) and `<->` (if and only if), which are rarely worth the confusion they cause. In practice, three shapes cover nearly everything: ```groovy agent { label 'linux' } // one capability agent { label 'linux && docker' } // conjunction of capabilities agent { label 'linux && !deprecated' } // exclude a retiring pool ``` Declarative pipelines write it as `agent { label '…' }`; scripted pipelines write `node('…') { … }`, taking the same expression string. Freestyle jobs have a "Restrict where this project can be run" field that accepts the same syntax. ## What the scheduler does with it When a build needs an executor, Jenkins builds the set of nodes satisfying the expression, filters out nodes that are offline, have no free executor, or are restricted from running that job, and assigns the build. Where several nodes qualify, the selection depends on the configured load-balancing strategy — do not promise an interviewer that Jenkins picks the least-loaded machine by CPU; the default behaviour is not a resource-aware scheduler, and you should not design around a specific choice. ## The no-match behaviour is the interesting half This is where the question usually goes. If no node satisfies the expression, the build **queues**. It does not error, it does not time out by default, and it does not fall back to any other node. Open the build queue in the UI and hover the pending item and Jenkins will tell you why it is stuck — typically that there are no nodes with the requested label, or that all matching nodes are offline or busy. Common root causes: - A typo, or a case mismatch: labels are case-sensitive, so `Linux` and `linux` are different labels. - The agent that carried the label was removed, renamed, or its label list was edited. - Every matching agent is offline — a disconnected agent still exists in the configuration, so the label "exists" but nothing can run. - The job is restricted by a folder or authorization setting from using the nodes that do match. Two defences matter. First, wrap pipelines in a timeout so a stuck build eventually fails loudly instead of sitting in the queue for a weekend: ```groovy options { timeout(time: 1, unit: 'HOURS') } ``` Note the important subtlety — a top-level `timeout` option in a declarative pipeline starts counting once the pipeline begins, so it does cover queue time for stage-level agents but you should verify the behaviour you rely on. Second, monitor queue depth and the age of the oldest queue item; a queue that only grows is the signal that a label no longer resolves. ## Clouds change the picture A configured cloud — the Kubernetes plugin, the EC2 plugin, the Docker plugin — advertises labels of its own through its templates. When a build asks for a label that only a cloud template provides, Jenkins provisions a new agent (a pod, an instance, a container), it connects, runs the build, and is torn down. From the Jenkinsfile the syntax is identical; the difference is that the label resolves to capacity that does not exist yet. This is why the same `agent { label 'linux' }` can mean "one of our four VMs" in one installation and "a fresh pod per build" in another. ## Designing a label taxonomy Treat labels as an interface between pipelines and infrastructure. Name capabilities (`docker`, `jdk17`, `arm64`, `gpu`), not owners or hostnames, so you can replace machines behind the label. Keep the vocabulary small and documented — every extra label is a way for a pipeline to become unschedulable when the fleet changes. When you retire a pool, add the new label to the new nodes *before* removing the old one, so no pipeline is stranded mid-migration.
- Why is pinning a stage to a node's own name usually a mistake?A node's name is an implicit label, so `node('build-07')` works — but it ties the pipeline to one machine. When that machine is rebuilt, renamed or retired the pipeline becomes unschedulable, and the scheduler loses any ability to balance work or fail over. Name the capability the build needs instead and let several nodes satisfy it.
- How would you detect that a label has stopped resolving before developers complain?Monitor the build queue: alert on queue depth and, more usefully, on the age of the oldest queue item, since a stuck label shows up as an item that never leaves. Add a pipeline `timeout` so individual builds fail loudly rather than waiting indefinitely, and treat 'blocked because there are no nodes with label X' in the queue reason as an actionable signal.
- How does a cloud plugin change what a label means at scheduling time?A cloud's agent template advertises labels for capacity that does not exist yet. When a build requests such a label and no online node matches, Jenkins asks the cloud to provision — a Kubernetes pod, an EC2 instance, a Docker container — which then connects as an agent, runs the build and is discarded. The Jenkinsfile syntax is unchanged; only where the capacity comes from differs.
saying these in an interview costs you the question
- Assuming an unmatched label makes the build fail fast
- Treating labels as case-insensitive
- Saying Jenkins picks the agent with the lowest CPU load
- Pinning pipelines to node names instead of capabilities
- Thinking one node can only carry a single label