How Jenkins Manages Workspace Allocation Across Agents: A Deep Dive into WorkspaceList

Jenkins isolates workspace allocation per agent using a thread-safe WorkspaceList that tracks directory locks through a lease-based system with automatic suffix generation (@2, @3, etc.) for concurrent builds.

When multiple builds run concurrently on the same agent, Jenkins must prevent directory collisions while allowing parallel execution. The jenkinsci/jenkins repository implements this through a sophisticated workspace allocation mechanism centered on the WorkspaceList class, which provides thread-safe reservation and per-agent isolation across the entire Jenkins environment.

The WorkspaceList Architecture

Per-Agent Workspace Isolation

Each Jenkins agent (represented by the Computer class) maintains its own WorkspaceList instance. This design guarantees that allocation decisions on one agent never interfere with another, even when jobs share identical names across different nodes.

Jenkins accesses these registries through two primary entry points:

  • Computer#getWorkspaceList() – Returns the WorkspaceList associated with that specific agent.
  • Jenkins#getWorkspaceFor(Job) – Convenience method that resolves the proper Computer for a job (considering assigned labels) and delegates to that agent's WorkspaceList.

The Allocation Algorithm

The core logic resides in WorkspaceList.allocate(FilePath base, Object context) inside core/src/main/java/hudson/slaves/WorkspaceList.java. The algorithm executes the following steps:

  1. Base path determination – The job defines a base directory (typically <agent-root>/workspace/<job-name>).
  2. Uniqueness iteration – The method iterates through suffixes @2, @3, and so on until finding a candidate directory that is either not locked or held by an entry sharing the same context (enabling parallel pipeline stages to reuse workspaces).
  3. Entry creation – Creates an Entry object stored in the internal inUse map, recording:
    • The owning thread (holder)
    • Allocation timestamp (time)
    • Stack trace for debugging (source)
    • Context object for re-entrancy
  4. Lease return – Returns a WorkspaceList.Lease implementing AutoCloseable. When the build finishes, the lease's release() method decrements the lock count and removes the entry from inUse.

Workspace Allocation Mechanisms

Normal vs. Quick Allocation

Jenkins supports two distinct allocation modes:

Normal allocation – The default path used by allocate(base, context). When the primary workspace is occupied, the allocator automatically adds numeric suffixes (@2, @3) until locating a free directory.

Quick allocation – Invoked via allocate(p, true, context) or acquire(base, true), this mode blocks other normal allocations from using that path until the quick lease releases. This mode suits short-lived operations (like sanity checks) that must not interfere with concurrent long-running builds.

Temporary Directory Handling

The WorkspaceList.tempDir(FilePath ws) method creates a sibling directory using the TMP_DIR_SUFFIX constant (@tmp). This provides transient storage for caches and intermediate files that persist across builds reusing the same workspace, but disappear when the workspace itself is cleaned.

Implementation Examples

Pipeline Usage (Declarative)

Standard pipeline syntax automatically handles workspace allocation through the node step:

node('my-agent') {
    def ws = pwd()  // Returns allocated path like /var/jenkins/workspace/my-job@2
    echo "Allocated workspace: ${ws}"
    sh "echo 'Building...' > build.log"
    // Workspace automatically released when node block exits
}

Direct WorkspaceList Access (Advanced Plugins)

Plugins requiring explicit control can interact with the WorkspaceList directly:

import hudson.FilePath;
import hudson.model.Computer;
import hudson.slaves.WorkspaceList;
import hudson.slaves.WorkspaceList.Lease;

Computer computer = // ... obtain target agent
WorkspaceList wsList = computer.getWorkspaceList();
FilePath base = new FilePath(computer.getRootPath(), "workspace/my-job");

// Context allows re-entrancy for parallel pipeline stages
Object context = build;

try (Lease lease = wsList.allocate(base, context)) {
    FilePath ws = lease.path;
    // Perform build steps
    ws.child("result.txt").write("Completed", "UTF-8");
}
// Lease automatically releases here via AutoCloseable

Quick Allocation for Short-Lived Tasks

Use quick allocation when you need exclusive access without generating new suffixed directories:

try (Lease quickLease = wsList.acquire(base, true)) {
    // Blocks other allocations; ideal for fast consistency checks
    performSanityCheck(quickLease.path);
}

Accessing Temporary Directories

Retrieve the associated temporary directory for cache storage:

FilePath tmp = WorkspaceList.tempDir(ws);
if (tmp != null) {
    tmp.mkdirs();
    // Store transient cache data
    tmp.child("maven-cache").mkdirs();
}

Key Source Files

The workspace allocation system spans these critical files in the jenkinsci/jenkins repository:

Summary

  • Each Jenkins agent maintains an isolated WorkspaceList that prevents cross-agent workspace collisions.
  • The allocation algorithm automatically appends numeric suffixes (@2, @3) to accommodate concurrent builds of the same job.
  • Lease objects implement AutoCloseable to ensure thread-safe lock release when builds complete.
  • Quick allocation provides exclusive short-term access without creating new directory suffixes.
  • Temporary directories (@tmp siblings) persist across builds but clean with the workspace.

Frequently Asked Questions

How does Jenkins prevent workspace conflicts when multiple builds run concurrently?

Jenkins uses the WorkspaceList class on each agent to track occupied directories. When a build requests a workspace, the allocate() method checks the internal inUse map and automatically generates unique directory names by appending @2, @3, etc., until finding an available path. Each allocation returns a Lease that locks the directory until the build releases it.

Can parallel pipeline stages share the same workspace?

Yes, parallel stages within the same pipeline can share a workspace if they pass the same context object (typically the Run instance) to the allocate() method. When the context matches an existing entry, Jenkins increments the lock count rather than creating a new suffixed directory, allowing true parallel execution within identical workspace paths.

What is the difference between normal and quick workspace allocation?

Normal allocation recursively searches for the next available suffixed directory when the primary workspace is occupied. Quick allocation, invoked with acquire(base, true) or allocate(path, true, context), blocks other normal allocations from using that specific path until the lease releases, making it ideal for short-lived operations that must not interfere with long-running builds.

Where does Jenkins store temporary files associated with a workspace?

Jenkins creates a sibling directory using the TMP_DIR_SUFFIX constant (set to @tmp) via WorkspaceList.tempDir(FilePath ws). This temporary directory survives across multiple builds that reuse the same workspace but is removed when the workspace itself is cleaned, providing a transient storage location for caches and intermediate files.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →