Skip to content

vserifoglu/ration

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

31 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ration

Run any script inside a CPU and memory budget. No code changes needed.

Platform Go License


The Problem

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.

The Solution

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 status
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

One sandbox can be designated priority — it gets its CPU allocation first. Everything else is rebalanced proportionally from what remains.


Requirements

  • 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.

WSL2 setup

Enable systemd in /etc/wsl.conf:

[boot]
systemd=true

Then restart WSL: run wsl --shutdown from PowerShell and reopen the terminal.

CPU controller delegation (required once, needs sudo)

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-reload

Then re-login or reboot.


Installation

Via go install (recommended):

go install github.com/vserifoglu/ration@latest

Requires 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/

Quick Start

Verify your system is ready:

ration doctor

Run a script with resource limits:

ration run train.py --cpu 80% --mem 16G

Check what's running:

ration status

Run 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 6G

When your priority job finishes, ration automatically restores the other sandboxes to their original allocations:

⚡ Priority sandbox exited. Remaining sandboxes restored.

Commands

ration run <script> [args...]

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 gate

Any arguments after the script name are passed through:

ration run train.py --epochs 100 --lr 0.001

ration status

Show 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

ration free

Show available CPU and memory across all running sandboxes.

ration bump <script>

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 24G

ration promote <script>

Promote a running sandbox to priority. Triggers CPU rebalancing across all sandboxes.

ration promote train.py

ration kill <script>

Stop a running sandbox. If it was priority, remaining sandboxes are rebalanced.

ration kill preprocess.py

ration doctor

Verify your system meets all requirements. Prints fix instructions for anything missing.

ration doctor
Checking system requirements...

  ✓ systemd running
  ✓ cgroups v2 available
  ✓ CPU controller delegated

CPU: 4 cores
Memory: 16.0G

✓ All checks passed. ration is ready to use.

ration log

Show the audit log of past runs from the current project directory (.ration/audit.jsonl).

ration log

ration watch

Watch a running sandbox's resource usage in real time.

ration watch train.py

Config

Global config — ~/.config/ration/config.toml

Sets 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"

Project config — ration.toml

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 heavy

Config priority: CLI flags override profile values, which override global defaults.

Interpreter mappings

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.


How It Works

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 CPUQuota in the cgroup. One priority sandbox gets its allocation first; all others share the remainder proportionally.
  • Memory is controlled via MemoryMax. ration blocks 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.

Troubleshooting

✗ 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.

About

Run any script inside a CPU and memory budget. No code changes needed.

Resources

License

Contributing

Stars

1 star

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors