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 theWorkspaceListassociated with that specific agent.Jenkins#getWorkspaceFor(Job)– Convenience method that resolves the properComputerfor a job (considering assigned labels) and delegates to that agent'sWorkspaceList.
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:
- Base path determination – The job defines a base directory (typically
<agent-root>/workspace/<job-name>). - 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). - Entry creation – Creates an
Entryobject stored in the internalinUsemap, recording:- The owning thread (
holder) - Allocation timestamp (
time) - Stack trace for debugging (
source) - Context object for re-entrancy
- The owning thread (
- Lease return – Returns a
WorkspaceList.LeaseimplementingAutoCloseable. When the build finishes, the lease'srelease()method decrements the lock count and removes the entry frominUse.
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:
core/src/main/java/hudson/slaves/WorkspaceList.java– Core implementation of the allocation algorithm, lease management, and temporary directory handling.core/src/main/java/hudson/model/Computer.java– Agent representation that owns theWorkspaceListinstance and providesgetWorkspaceList().core/src/main/java/jenkins/model/Jenkins.java– Master-level coordination viagetWorkspaceFor(Job).core/src/main/java/hudson/model/AbstractProject.java– Integration point for Freestyle and Pipeline jobs.core/src/main/java/hudson/model/Run.java– Build-level workspace storage viaRun#getWorkspace().
Summary
- Each Jenkins agent maintains an isolated
WorkspaceListthat 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
AutoCloseableto ensure thread-safe lock release when builds complete. - Quick allocation provides exclusive short-term access without creating new directory suffixes.
- Temporary directories (
@tmpsiblings) 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →