The cloud,
without the cloud.
Decorate a Python function. Run it on the machines you already own. roost ships your code to your fleet, provisions the environment once, streams the logs back, and records every run in a local ledger. No account. No upstream. No per-second meter.
$ roost run train.py::train '{"epochs": 5}'✓ building image im-8IYXJOFyr4 · uv · cached✓ created objects · train on fleet:jl1[epoch 1/5] loss=1.7412[epoch 5/5] loss=0.2183[done] checkpoints committed to volume✓ completed in 6.32s · ran_on jl1
Modal's ergonomics. Your hardware's economics.
The same decorate-and-run workflow developers love — pointed at machines you control, with nothing hidden behind a dashboard you don't own.
Portable by design
roost ships your function's source plus plain-pickled args — never a code object. A run works across CPython versions, on any node, no matched interpreter required.
Environments built once
Images hash to a digest that names a cached venv on each node. Unchanged image, zero rebuild — provisioned once, reused forever. Sub-5s cold builds with uv.
Radical transparency
Modal masks the host to modal. roost tells you the truth: every run records the real node it executed on. The ledger lives in ~/.roost — it's yours.
Four stages, bookended.
Every roost run streams the same shape — the client always knows where the work is.
Assemble the image
The image digest names a venv. Missing on the target? roost builds it with uv and marks it ready. Present? Skipped entirely.
Ship source + args
Your source file, sibling modules, and pickled args are staged to the node over SSH. No cloudpickle, no version lock-in.
Run & stream
The function runs in the provisioned env. stdout and stderr stream back live and land in the run's log files.
Commit to the ledger
Status, duration, host, and result are written to meta.json. roost runs and the console read straight from it.
Same primitives. Different ownership.
modalran_on jl1~/.roost)@app.function(gpu=…)@app.function(gpu="fleet:jl1")