# How Jenkins Manages Build Queue Scheduling and Load Balancing: A Deep Dive into the Core Queue Engine

> Learn how Jenkins manages build queue scheduling and load balancing with its core Queue class. Discover internal states and customized load balancing for efficient task assignment.

- Repository: [Jenkins/jenkins](https://github.com/jenkinsci/jenkins)
- Tags: deep-dive
- Published: 2026-06-19

---

**Jenkins manages build queue scheduling and load balancing through the `hudson.model.Queue` class, which maintains four internal states (waiting, blocked, buildable, pending) and assigns tasks to executors using a consistent-hashing load balancer that can be customized via extension points.**

The jenkinsci/jenkins repository contains a sophisticated scheduling engine that orchestrates millions of builds across distributed executor pools. Understanding how Jenkins build queue scheduling and load balancing works is essential for administrators optimizing CI/CD performance and developers writing custom plugins. At the heart of this system lies the `hudson.model.Queue` class, which coordinates thread-safe state transitions and executor assignment through a series of extensible extension points.

## Understanding the Jenkins Queue Architecture

The core queue implementation in [`hudson/model/Queue.java`](https://github.com/jenkinsci/jenkins/blob/main/hudson/model/Queue.java) organizes build tasks into four distinct internal collections, each representing a specific lifecycle stage. All state transitions are protected by a `ReentrantLock` (`Queue.lock`) to ensure thread safety, while a `Queue.snapshot` object provides the UI with consistent read-only views without blocking the scheduling thread.

### Internal Queue States and Collections

When a build enters the system, it progresses through these states:

- **waitingList** – Items respecting their quiet period before becoming eligible for execution.
- **blockedProjects** – Items that cannot run due to upstream/downstream constraints, node-level limits, or a `QueueTaskDispatcher` veto.
- **buildables** – Items ready to be assigned to an idle executor.
- **pendings** – Items that have been offered to an executor but have not yet started running.

## How the Build Queue Scheduling Flow Works

According to the jenkinsci/jenkins source code, the scheduling engine follows a precise six-step cycle driven by the `MaintainTask` periodic timer:

### 1. Task Entry and Quiet Periods

When a build is triggered via `Queue.schedule(Task, int)`, the task is wrapped in a `Queue.Item` and inserted into the `waitingList` until its quiet period expires.

### 2. Maintenance Cycle Execution

A periodic `MaintainTask` (initialized in `Queue`'s constructor) invokes `Queue.maintain()`. This method moves items from *waiting* → *blocked* → *buildable* based on resource availability and dispatcher checks.

### 3. Blocking Checks and Validation

The `Queue.getCauseOfBlockageForItem` method iterates over all registered `QueueTaskDispatcher` extensions (`QueueTaskDispatcher.all()`) and checks node-level constraints via `Node.canTake`. If any dispatcher returns a non-null `CauseOfBlockage`, the item remains in the *blocked* list.

### 4. Load-Balancing and Executor Assignment

Once an item is ready, the queue creates a `MappingWorksheet` containing all idle executors as `ExecutorChunk` objects. The `LoadBalancer` (default is `LoadBalancer.CONSISTENT_HASH`) decides which executor(s) will run the task by building a consistent hash of available executors and assigning the task greedily.

The default implementation weights executors by their capacity (`ec.size() * 100`) to distribute load proportionally:

```java
// Simplified excerpt from LoadBalancer.CONSISTENT_HASH.map(...)
List<ConsistentHash<ExecutorChunk>> hashes = new ArrayList<>(ws.works.size());
for (int i = 0; i < ws.works.size(); i++) {
    ConsistentHash<ExecutorChunk> hash = new ConsistentHash<>(ExecutorChunk::getName);
    List<ExecutorChunk> chunks = ws.works(i).applicableExecutorChunks();
    Map<ExecutorChunk,Integer> toAdd = Maps.newHashMapWithExpectedSize(chunks.size());
    for (ExecutorChunk ec : chunks) {
        toAdd.put(ec, ec.size() * 100);   // weight = #executors
    }
    hash.addAll(toAdd);
    hashes.add(hash);
}
// greedy assignment → Mapping

```

### 5. Sorting Buildable Items

Before items are offered to executors, the `QueueSorter` sorts the `buildables` list. The default sorter orders items by FIFO, but a custom sorter can be installed via `Queue.setSorter`.

```java
// QueueSorter.sortBuildableItems (abstract)
public abstract void sortBuildableItems(List<Queue.BuildableItem> buildables);

```

### 6. Execution and State Transition

The selected `Executor` receives a `JobOffer`, calls `start(workUnit)`, and the `WorkUnit` runs the `Task`. When execution begins, `Queue.onStartExecuting` moves the item from `pendings` to the `LeftItem` cache.

## Customizing Jenkins Build Queue Behavior

The queue exposes three primary extension points for customizing Jenkins build queue scheduling and load balancing:

### Implementing a Custom LoadBalancer

You can override the default consistent-hashing strategy by extending `LoadBalancer` and registering it with `Queue.setLoadBalancer()`:

```java
@Extension
public class MyLoadBalancer extends LoadBalancer {
    @Override
    public Mapping map(@NonNull Task task, MappingWorksheet ws) {
        // Simple round-robin: pick the first idle executor for every work chunk
        Mapping m = ws.new Mapping();
        for (int i = 0; i < ws.works.size(); i++) {
            ExecutorChunk ec = ws.works(i).applicableExecutorChunks().get(0);
            m.assign(i, ec);
        }
        return m;
    }
}

// Enable it at runtime
Queue q = Jenkins.get().getQueue();
q.setLoadBalancer(new MyLoadBalancer());

```

### Creating a Priority-Based QueueSorter

To change the dispatch order from FIFO to priority-based, implement `QueueSorter`:

```java
@Extension
public class PrioritySorter extends QueueSorter {
    @Override
    public void sortBuildableItems(List<Queue.BuildableItem> buildables) {
        buildables.sort(Comparator.comparingInt(item -> {
            // Assume each job has a "priority" parameter
            return Integer.parseInt(item.task.getProperty("priority"));
        }));
    }
}

```

### Using QueueTaskDispatcher for Access Control

Implement `QueueTaskDispatcher` to veto builds based on external conditions like maintenance windows or license availability:

```java
@Extension
public class MaintenanceDispatcher extends QueueTaskDispatcher {
    @Override
    public CauseOfBlockage canRun(Queue.Item item) {
        if (System.currentTimeMillis() < maintenanceEnd) {
            return CauseOfBlockage.fromMessage("Jenkins is in maintenance mode");
        }
        return null; // allow execution
    }
}

```

## Scheduling Builds Programmatically

You can interact directly with the queue from Groovy scripts or management commands:

```groovy
def job = Jenkins.instance.getItemByFullName('example-job')
def task = job.asJob()          // implements Queue.Task
def queue = Jenkins.instance.queue
queue.schedule(task, 0)         // 0s quiet period → immediate entry

```

This invokes `Queue.schedule(Task,int)` and immediately inserts the task into the scheduling pipeline.

## Summary

- **Jenkins build queue scheduling** is orchestrated by `hudson.model.Queue`, which maintains thread-safe state transitions across four collections: `waitingList`, `blockedProjects`, `buildables`, and `pendings`.
- **Load balancing** uses a consistent-hashing algorithm by default (`LoadBalancer.CONSISTENT_HASH`), weighting executors by their capacity to distribute tasks proportionally.
- **Extension points** allow customization of scheduling behavior through `LoadBalancer`, `QueueSorter`, and `QueueTaskDispatcher` implementations.
- **State protection** relies on `Queue.lock` (a `ReentrantLock`) and `Queue.snapshot` for consistent UI updates without blocking the scheduler.
- **Programmatic access** is available via `Queue.schedule()` for automation and scripted pipeline management.

## Frequently Asked Questions

### How does Jenkins decide which executor runs a build?

Jenkins uses the `LoadBalancer.CONSISTENT_HASH` implementation by default, which creates a consistent hash ring of all idle `ExecutorChunk` objects (grouped by node). The algorithm assigns weights based on executor count (`ec.size() * 100`) and maps tasks to executors using greedy assignment. You can customize this behavior by implementing the `LoadBalancer` extension point and registering it via `Queue.setLoadBalancer()`.

### What is the difference between blocked and buildable states in the Jenkins queue?

Items in the `blockedProjects` list cannot run due to external constraints checked by `QueueTaskDispatcher` implementations or node-level limits (`Node.canTake`). Items in the `buildables` list have passed all blocking checks and are ready for immediate executor assignment. The `Queue.maintain()` method periodically moves items between these states based on current resource availability and dispatcher evaluations.

### Can I change the default FIFO ordering of the Jenkins build queue?

Yes. While the default `QueueSorter` uses FIFO ordering, you can implement a custom sorter by extending `hudson.model.queue.QueueSorter` and overriding `sortBuildableItems(List<Queue.BuildableItem>)`. Register your implementation with `Queue.setSorter()` to reorder builds based on priority, job duration estimates, or custom business logic before the load balancer assigns them to executors.

### How do I prevent specific jobs from running during maintenance windows?

Implement a `QueueTaskDispatcher` extension that overrides `canRun(Queue.Item)`. Return a `CauseOfBlockage` object when your maintenance condition is active, or return `null` to allow execution. Because Jenkins checks all registered dispatchers before moving items from `blockedProjects` to `buildables`, this effectively vetoes builds during the specified window without modifying job configurations.