You’ve embraced the power of Jenkins Declarative Pipelines, and now you’re looking to take the next step in CI/CD optimization: running stages in parallel. The promise is alluring—slashing your build times by running tests, linting, and security scans simultaneously. You eagerly refactor your Jenkinsfile
, commit the change, and watch your pipeline trigger. But instead of blazing-fast execution, your build gets stuck, silently hanging with no obvious error.
This frustrating scenario is often caused by two insidious problems: executor starvation and node label mismatch. While the parallel
step in Jenkins is incredibly powerful, using it naively can lead to deadlocked pipelines and misconfigured jobs that fail in subtle ways. Understanding how Jenkins allocates resources is the key to unlocking true parallelism without the headaches. This guide will walk you through these common pitfalls and provide the best practices to avoid them.
The Power and Simplicity of the parallel
Step
In a Jenkins Declarative Pipeline, the parallel
directive allows you to define a set of stages that Jenkins will execute concurrently. This is a perfect fit for tasks that are independent of each other.
For instance, instead of running unit tests, then code quality analysis, then packaging an application sequentially, you can run them all at once. A simple implementation involves defining a primary stage and then, within it, a parallel
block. Inside this block, you define each concurrent task as its own nested stage, such as ‘Unit Tests’, ‘Linting’, and ‘Security Scan’.
When this pipeline runs, these nested stages will all start at the same time, each on its own executor thread. The pipeline will only proceed to the next primary stage once all three parallel stages have completed successfully. The potential for time savings is enormous. However, this simple approach hides a critical flaw.
The Silent Killer: Jenkins Executor Starvation
The most common reason a Jenkins parallel pipeline gets stuck is executor starvation. This is a classic deadlock scenario that can be tricky to diagnose.
What is Executor Starvation?
- Executors are Finite: A Jenkins agent (or node) has a configured number of “executors,” which are essentially slots for running a job or a stage. If an agent has 4 executors, it can run 4 tasks concurrently.
- Parent Job Holds an Executor: When your main pipeline job starts, it consumes one executor on an agent.
- Parallel Stages Demand More Executors: When the pipeline reaches the
parallel
block, it tries to launch each nested stage. Each of these new stages also requires its own executor. - The Deadlock: In our example, the parent pipeline is using 1 executor. It then tries to launch 3 more stages. In total, this single build now requires 4 executors. If the agent it’s running on only has 2 or 3 executors available, the parallel stages will be stuck in the queue, waiting for a free executor. But no executor will ever become free, because the parent pipeline is holding its own executor while it waits for the child stages to complete—stages that can never start.
Your build is now deadlocked, a victim of executor starvation.
Solving Executor Starvation: The agent none
Pattern
The most effective solution is to structure your pipeline so the parent job doesn’t hold an executor while the parallel work is happening. You can achieve this by using agent none
at the top level of your pipeline and defining an agent
for each stage individually.
In this model, the main pipeline orchestrates the flow but doesn’t occupy a permanent executor slot. Each stage, including the parallel ones, is scheduled independently by Jenkins. They will acquire an executor from any available agent that matches a specified label, run their task, and then release it. This completely eliminates the deadlock scenario.
The Mismatch Problem: Node and Label Confusion
The second major pitfall is running a parallel stage on the wrong type of agent. This happens when you have a heterogeneous Jenkins environment with agents labeled for specific purposes (e.g., windows
, docker
, maven
).
A node label mismatch occurs when a stage that requires a specific tool (like Docker) is accidentally scheduled on an agent that doesn’t have it installed. This leads to build failures that can be confusing, as the pipeline syntax might look correct at first glance.
Best Practices for Agent Labeling in Parallel
1. Be Explicit in Every Parallel Stage
The clearest and safest approach is to explicitly define the required agent
inside each parallel stage
, just as described in the agent none
pattern. For example, in a cross-platform testing scenario, you would have a parallel
block containing two stages. The first stage would explicitly request an agent with the windows-tester
label, while the second stage would request an agent with the linux-tester
label. This leaves no ambiguity and ensures Jenkins knows exactly where to run each part of your build.
2. Leverage the Jenkins Kubernetes Plugin
For ultimate flexibility, use the Jenkins Kubernetes plugin to dynamically provision agents as pods for your pipeline runs. This is ideal for CI parallel builds because you can define a purpose-built environment for each task.
Each parallel stage can define its own pod template, specifying the exact container image, resources, and tools it needs. For example, one parallel stage could define a pod that uses a Maven container image, while another simultaneously running stage could define a pod that uses a Node.js image.
With this approach, node mis-labelling becomes a non-issue. You aren’t depending on pre-configured, static agents; you’re creating the perfect, ephemeral agent on-demand for every single stage.
Advanced Controls for Robust Parallelism
failFast: true
: Add this option to yourparallel
block. If any stage fails, Jenkins will immediately abort all other stages in the parallel block. This saves precious CI resources and provides faster feedback.timeout
: Wrap critical stages in atimeout
option to prevent a single hung job from blocking your entire pipeline.- Throttle Builds and Lockable Resources: If your parallel stages compete for a finite external resource (like a shared test database or a staging environment), executor management alone won’t solve the problem. Use the Throttle Concurrent Builds or Lockable Resources plugins to ensure only a certain number of stages can access that shared resource at once.
By adopting these patterns—using agent none
, defining agents explicitly within each parallel stage, and leveraging tools like the Kubernetes plugin—you can harness the full speed of parallel execution without falling into the common traps of executor starvation and agent mismatch.
A fast pipeline relies on a healthy infrastructure. To diagnose issues like agent resource contention or a full build queue, you need deep visibility into your Jenkins controller and agents. Netdata provides real-time monitoring for your entire CI/CD infrastructure, helping you spot performance bottlenecks before they slow your developers down.