Run any script inside a CPU and memory budget. No code changes needed.
You have multiple long-running scripts — training jobs, preprocessing pipelines, data loaders — all competing for the same CPU and memory. AI agents running parallel tasks make this worse: each tool call can spawn a subprocess, and there's nothing stopping them from collectively saturating the machine. There's no easy way to say "give this 80% of the machine and cap everything else." You end up babysitting htop, killing things manually, or just hoping for the best.
ration wraps any script in a resource-limited sandbox using Linux cgroups v2 and systemd transient scopes. The script runs exactly as it would normally — you just tell ration how much CPU and memory it's allowed to use.
# Run train.py with 80% CPU and 16 GB memory as a priority job
ration run train.py --cpu 80% --mem 16G --profile priority
# Run a lighter preprocessing job alongside it
ration run preprocess.py --cpu 40% --mem 6G
# Check what's running
ration statusScript PID CPU Memory Profile
────── ─── ─── ────── ───────
train.py 18234 3.2/4 (80%) 2.1G/16.0G priority
preprocess.py 18291 1.6/4 (40%) 512.0M/6.0G
One sandbox can be designated priority — it gets its CPU allocation first. Everything else is rebalanced proportionally from what remains.
- Linux (native or WSL2 with systemd enabled)
- systemd with user session support
- cgroups v2 (unified hierarchy, mounted at
/sys/fs/cgroup) - CPU controller delegated to your user session
Run ration doctor to verify all requirements and get fix instructions for anything missing.
Enable systemd in /etc/wsl.conf:
[boot]
systemd=trueThen restart WSL: run wsl --shutdown from PowerShell and reopen the terminal.
sudo mkdir -p /etc/systemd/system/[email protected]
echo -e '[Service]\nDelegate=cpu cpuset memory pids' | \
sudo tee /etc/systemd/system/[email protected]/delegate.conf
sudo systemctl daemon-reloadThen re-login or reboot.
Via go install (recommended):
go install github.com/vserifoglu/ration@latestRequires Go 1.22+. The binary is placed in ~/go/bin/ — make sure that's in your PATH.
Build from source:
git clone https://github.com/vserifoglu/ration.git
cd ration
go build -o ration .
sudo mv ration /usr/local/bin/Verify your system is ready:
ration doctorRun a script with resource limits:
ration run train.py --cpu 80% --mem 16GCheck what's running:
ration statusRun a second script alongside it. If the first was marked priority, the second gets whatever CPU is left:
ration run preprocess.py --cpu 40% --mem 6GWhen your priority job finishes, ration automatically restores the other sandboxes to their original allocations:
⚡ Priority sandbox exited. Remaining sandboxes restored.
Run a script inside a resource-limited sandbox.
| Flag | Description | Example |
|---|---|---|
--cpu |
CPU allocation: percentage, cores, or decimal | 80%, 4, 2.5 |
--mem |
Memory limit: bytes with suffix or percentage | 16G, 512M, 60% |
--profile |
Named profile from ration.toml |
--profile heavy |
--force |
Override memory over-allocation protection |
ration run train.py --cpu 80% --mem 16G
ration run eval.py --profile heavy --cpu 60% # CLI flags override profile
ration run train.py --cpu 80% --mem 32G --force # bypass memory gateAny arguments after the script name are passed through:
ration run train.py --epochs 100 --lr 0.001Show all running sandboxes with real-time resource usage.
Script PID CPU Memory Profile
────── ─── ─── ────── ───────
train.py 18234 3.2/4 (80%) 2.1G/16.0G priority
preprocess.py 18291 1.6/4 (40%) 512.0M/6.0G
Show available CPU and memory across all running sandboxes.
Adjust resource limits on a running sandbox without restarting it.
| Flag | Description |
|---|---|
--cpu |
New CPU allocation |
--mem |
New memory limit |
ration bump train.py --cpu 60%
ration bump train.py --mem 24GPromote a running sandbox to priority. Triggers CPU rebalancing across all sandboxes.
ration promote train.pyStop a running sandbox. If it was priority, remaining sandboxes are rebalanced.
ration kill preprocess.pyVerify your system meets all requirements. Prints fix instructions for anything missing.
ration doctorChecking system requirements...
✓ systemd running
✓ cgroups v2 available
✓ CPU controller delegated
CPU: 4 cores
Memory: 16.0G
✓ All checks passed. ration is ready to use.
Show the audit log of past runs from the current project directory (.ration/audit.jsonl).
ration logWatch a running sandbox's resource usage in real time.
ration watch train.pySets system-wide defaults used when no project config or CLI flags are provided.
[system]
reserved_mem = "2G" # memory kept free for the OS (default: 2G)
retention_size = "5M" # max size of .ration/audit.jsonl (default: 5M)
[defaults]
cpu = "50%"
mem = "8G"Place ration.toml in your project root (or any parent directory — ration walks up from the current directory). Defines named profiles and interpreter mappings.
[profiles.heavy]
cpu = "80%"
mem = "16G"
priority = true
[profiles.light]
cpu = "20%"
mem = "4G"
mem_warning = "75%" # warn when sandbox reaches 75% of its mem limit
[interpreters]
py = "python3"
sh = "bash"Use a profile with --profile:
ration run train.py --profile heavyConfig priority: CLI flags override profile values, which override global defaults.
ration can automatically prepend an interpreter for scripts that aren't directly executable. Map file extensions (without the dot) to interpreter commands in [interpreters]:
[interpreters]
py = "python3"
sh = "bash"With this config, ration run train.py launches python3 train.py inside the sandbox.
ration uses systemd transient scopes (via systemd-run) to create a cgroup for each sandbox. The Linux kernel enforces the limits directly — no polling, no wrappers around the script process.
- CPU is controlled via
CPUQuotain the cgroup. One priority sandbox gets its allocation first; all others share the remainder proportionally. - Memory is controlled via
MemoryMax.rationblocks launch if the total allocated memory would exceed available RAM minus a reserved headroom (default 2 GB). CPU over-allocation is non-fatal — the kernel throttles proportionally. - State is stored entirely in the systemd unit description as a metadata string (
ration:script:cpu=X:mem=Y:...). No external database or daemon required. - OOM kills are detected by reading the systemd unit result after exit, and a diagnostic report is printed showing peak memory and throttle time.
✗ CPU controller is not delegated
CPU limits won't be applied until you delegate the CPU controller to your user session. Run ration doctor for the exact fix command (one-time, requires sudo).
Script was OOM-killed
ration will print a report showing peak memory and the limit that was hit:
✗ train.py was OOM-killed
Memory limit: 4.0G
Peak usage: 3.9G
Runtime: 1m23s
Increase --mem or switch to a profile with a higher memory limit.
Running on WSL2 without systemd
Add systemd=true under [boot] in /etc/wsl.conf, then run wsl --shutdown from PowerShell and reopen. See Requirements above.