Run several coding agents on one Linux machine without it grinding to a halt.

Each agent sizes its builds and test runs for the whole machine, so four at once ask for four machines. leasehold makes a heavy command take a lease on a share of the machine first: it waits its turn when the machine is full, runs sized to its share, and, where systemd allows it, runs in a cgroup weighted to that share. What an agent leaves running is stopped when it exits.

uv tool install leasehold

Or try it in 30 seconds without installing: uvx leasehold demo

One machine, twelve slots

Illustration: a scripted sequence, not a live machine
Held
Waiting
work, reserves 4, max 6external, max 8lend, preemptibleobservedreserved, free
1 / 8

Four kinds of lease

Every lease weighs one or more slots and lives as long as its holder keeps renewing it. A holder that dies stops renewing, and its slots come back on their own.

work

The owner's own work: builds, tests, agent tool calls. Its pool can reserve slots nobody else may take.

external

Work that arrives from outside: CI runner jobs, test gates, other people's scripts.

lend

Spare capacity lent out only while the machine is idle, and revoked the moment other work needs it back.

observed

A known app someone started outside leasehold, counted against the machine so others wait for it.

Who it is for

First, one developer running several coding agents on one machine. The same daemon also serves machines that people and CI runners share.

Several coding agents

Agents start builds and test runs whenever they like. Under leasehold run, their heavy commands take turns by agent session, sized to the share they hold.

leasehold run -- claude

Test suites

A parallel test run sized for an empty machine is wrong on a busy one. A lease sizes the run to the slots it holds, and the next suite waits its turn.

leasehold hold -- pytest -n auto

CI runners on a shared box

Several self-hosted runners on one machine start their jobs together and starve each other and the people working there. Each job step holds a lease, so heavy jobs queue instead of piling up.

leasehold hold --kind external -- make test

People sharing a workstation

A lab machine or a team build box. Everyone can see what holds the machine and why their job waits, in a terminal or a browser.

leasehold top

What it changes, measured

Eight simulated agents start test runs and memory-hungry builds at random on an 8-CPU, 16 GiB machine. The same 52 jobs each time.

Unmanagednice, a slice or -j4leasehold
Jobs killed by the OOM killer05 to 90
git status, p95981 ms970 to 1130 ms103 ms
How late a 10 ms sleep wakes, p9925 ms19 to 27 ms3.3 ms
Swap read back2.8 GiB4.9 to 9.8 GiB0.2 GiB
Time for all the work566 s579 to 630 s848 s

The machine stays usable and nothing is lost, at a price: the work took up to 1.5 times as long, because heavy jobs wait their turn instead of overlapping. One machine and one run per condition so far. Method, full tables and caveats.

Quickstart

Linux with Python and uv. The daemon runs as you; nothing needs root.

  1. Install the command-line tool.

    uv tool install leasehold
  2. Set up: a config sized for this machine, your own daemon as a systemd user service, and the PATH shims. Safe to repeat; --remove undoes it.

    leasehold setup
  3. Run an agent as one session. Its heavy commands take leases; what it leaves running is stopped when it exits.

    leasehold run -- claude
  4. Watch who holds the machine, who waits and why.

    leasehold top
Thirty-second tryuvx leasehold demoruns a daemon and a scripted crowd of jobs, then shows them taking turns.

What it looks like

The terminal view and the web view show the same thing: who holds the machine, who waits and why, and how much pressure it is under.

leasehold top in a terminal on a busy twelve-slot machine: an observed data-prep program pushes it two slots over its ceiling, pools with held and maximum slots, a table of leases, and three queued leases each with the reason it waits.
Screenshot pending
media/top.png
leasehold top in a terminal
The leasehold web view in the dark theme on a busy machine: nine of twelve slots held, three leases waiting, each with its reason, and CPU pressure above the lending limit.
Screenshot pending
media/ui-dark.png
leasehold ui, dark theme
The same moment in the light theme: nine of twelve slots held, three leases waiting with their reasons, pressure sparklines for CPU, memory and IO.
Screenshot pending
media/ui-light.png
leasehold ui, light theme

For agents

Coding agents are the users most likely to start three builds at once. leasehold meets them where they already run commands.

Claude Code plugin

Takes a lease around an agent's heavy commands and labels it with the agent session, so one person's sessions take turns with each other as well as with everyone else.

PATH shims

Stand-ins for build and test commands that run the real command under a lease. Agents and scripts get turn-taking without changing how they call their tools.

MCP server

Lets an agent ask how busy the machine is, why its work is waiting, and hold a lease, through MCP tools instead of parsing terminal output.

Jobserver sizing

A lease can size a GNU make jobserver, so make -j and other jobserver-aware tools run as many jobs as the lease holds slots, not as many as the machine has cores.

Examples

Each one is a short, runnable setup in the repository.

  • GitHub runnerSelf-hosted runners on one box, each job step holding an external lease weighted by how heavy it is.
  • Test suite and make -jA test run and a parallel build sized to the slots they hold.
  • Several agents, fairlyTwo users and four agent sessions on one machine, taking turns per user and then per session.
  • Lending and revocationAn idle machine lends slots; other work arrives and the lent lease is revoked.
  • Library-only useThe admission policy as a Python library, with no daemon.
  • Observed appsA rule that counts a known app started outside leasehold, so others wait while it runs.

How it compares

Most of these are good tools that solve a nearby problem. Pick the one whose shape fits yours.

ToolWhat it doesNext to leasehold
flock, semutil-linux, GNU parallelOne lock file, or a counting semaphore of N slots, around a command.No weights, users, sessions or load. Enough for one person and one kind of job; leasehold adds turn-taking across users and weighted slots.
pueue, task-spooler, nqHand a command to a queue that runs it later, with a limit on how many run at once.They own and run the job; leasehold only admits it, so the job keeps running in your shell, CI step or agent. Their queues are per user, not shared fairly across users.
consul lockA lock or semaphore held in a Consul cluster's key-value store.Spans many machines but needs a Consul cluster, and knows nothing about the machine's load.
systemd slicescgroup weights and limits for the CPU, memory and IO of running processes.Shares resources among what already runs; never makes work wait to start. They combine well: slices shape running work, leasehold decides when it starts.
Slurm, HTCondor, NomadCluster schedulers: submit jobs, place them on nodes, account for use.Built for many machines and much more to run. leasehold suits the one shared box where a cluster scheduler would be out of proportion.
agent-semaphoreCoordinates parallel coding agents on one repository: claims on code scopes, conflict warnings, a landing queue.About who edits and merges what, not the machine: it does not limit how many builds and test runs share the CPUs and memory. The two fit together.
stevesystem-wide jobserverOne GNU make jobserver shared by every jobserver-aware build on the machine.Reaches only tools that speak the jobserver protocol, with no users, pools or expiry. leasehold can size a jobserver from a lease instead.
GitHub Actions concurrencyAt most one running job per concurrency group in a repository or organisation.Knows workflows, not the machine: it cannot see other users, agents or the load on a self-hosted runner's box.

What it is not

Not a sandbox

Where a systemd user manager is available, each command runs in a scope weighted to its lease, with soft memory limits (enforced). Elsewhere leases and sizing still apply, but a command that ignores its job count is not held back (advisory). leasehold doctor says which.

Not a scheduler

It never runs, places or retries your jobs. It says when they may start and how many slots they may hold; your shell, CI runner or agent runs them.

One machine only

One daemon per machine, with no cluster, no federation and no network of daemons. Several machines means several independent daemons.

Linux only

It reads pressure stall information from /proc/pressure and works with cgroup v2. It does not run on macOS or Windows.

Reference

The full reference, configuration and API live in the repository: README and the reference.

leasehold setup
Config, your own daemon and the PATH shims, in one step.
leasehold run -- <agent>
Run an agent as one session: its heavy commands leased, its leftovers stopped.
leasehold hold -- <cmd>
Wait for a lease, run the command while holding it, release it on exit.
leasehold top
Live terminal view of slots, pools, leases and the queue.
leasehold ui
Print a local link to the web view, with its access token.
leasehold demo
Run a daemon and a scripted crowd of jobs to watch them take turns.