Runtimes
Every job runs in a Simulo runtime: a prebuilt environment on the GPU machine that already has the simulator, the GPU drivers and libraries, PyTorch, NumPy, and the rest of the scientific Python stack. Simulo builds and maintains it; you never build or push an image.
A simulo.Runtime can add two things on top:
public PyPI packages with pip_install(), and environment variables with
env(). Most apps need neither.
The default runtime
Section titled “The default runtime”An app that names no runtime runs on the default, simulo/gpu-rl:2026.06:
import simulo
app = simulo.App("cartpole") # the default runtimeTo name a runtime explicitly, pick it from the ones Simulo offers:
app = simulo.App( "cartpole", runtime=simulo.Runtime.from_registry("simulo/gpu-rl:2026.06"),)Runtime.from_registry(name) chooses among Simulo’s runtimes. You can’t
bring your own image or registry. If your job needs something the base itself
must provide, such as a different CUDA version or a compiled system library,
that is a request to the Simulo team.
A name Simulo doesn’t offer is normally refused before anything uploads, and the message lists the runtimes that exist. If the platform can’t tell yet, you get a warning instead and the job runs on the default runtime.
✗ unknown runtime 'simulo/python-cpu:3.11' — this platform does not offer it; available runtimes: simulo/gpu-rl:2026.06simulo run makes this check automatically, and
simulo prepare runs the same check on
its own. An app that leaves out runtime= always gets the default and never
hits it.
Add PyPI packages: Runtime.pip_install()
Section titled “Add PyPI packages: Runtime.pip_install()”pip_install() adds public PyPI packages the base runtime doesn’t ship. Simulo
installs them on the GPU machine before your job starts:
import simulo
runtime = simulo.Runtime.from_registry("simulo/gpu-rl:2026.06").pip_install("shapely")app = simulo.App("zone-approach", runtime=runtime)
with app.runtime.imports(): import torch # from the base runtime from shapely.geometry import Point, Polygon # added by pip_install()Build a dedicated Runtime with Runtime.from_registry(...) and pass it to
App(runtime=...). Calling pip_install() on app.runtime of an app that
left out runtime= would change the default runtime object that every such
app shares.
Each call adds one layer, and layers install in the order you declared them:
runtime = ( simulo.Runtime.from_registry("simulo/gpu-rl:2026.06") .pip_install("shapely") .pip_install("wandb==0.17.0", pre=False))The rules:
- Public PyPI only. Each argument is a plain requirement: a name,
optionally with extras and a version, such as
"shapely"or"transformers[torch]>=4.40". URLs,git+references, local paths, andname @ urlreferences are refused when you callpip_install().pre=Trueallows pre-release versions for that layer. - No replacing base packages. Naming a well-known base package, such as
torch,numpy, the CUDA packages, or thesimulopackages, fails the submit with a list of the offenders. Any other package the base already provides fails the job on the GPU machine before your code runs. A second copy of a base package can break the runtime in ways your own traceback would never explain. - Your packages come last. They are added after the base environment, so the runtime’s own versions always win.
pip_install()with no arguments does nothing.
Set environment variables: Runtime.env()
Section titled “Set environment variables: Runtime.env()”env() adds environment variables to the job. Later calls win when keys
repeat, and it chains like pip_install():
runtime = simulo.Runtime.from_registry("simulo/gpu-rl:2026.06").env( {"WANDB_PROJECT": "cartpole", "WANDB_MODE": "offline"})A small set of names belongs to the platform: PATH, PYTHONPATH, HOME,
PIP_* names, and specific SIMULO_* names and prefixes that carry mount paths, job
identity, and operator settings. A reserved key is skipped with a warning,
and your job still runs. Other SIMULO_* names are yours to set, including
SIMULO_CHECKPOINT_EVERY, which changes how often a training job saves
checkpoints. A checkpoint_every= passed to train() takes precedence over
it.
Heavy imports: app.runtime.imports()
Section titled “Heavy imports: app.runtime.imports()”simulo run imports your file on your own machine, where heavy packages such
as torch usually aren’t installed. Write those imports once, at module
level, inside app.runtime.imports():
with app.runtime.imports(): import torch # resolved only on the GPU machine- On your machine, the import is recorded and skipped, so submitting stays fast and needs nothing heavy installed.
- On the GPU machine, it is an ordinary import.
A heavy import anywhere else at module level would break simulo run on a
machine without that package. Imports inside a job body or a task method need
no guard, since those never run on your machine.
Compiled kernels: @app.runtime.torch_jit
Section titled “Compiled kernels: @app.runtime.torch_jit”A reward or observation function that benefits from torch.jit.script can be
a module-level function decorated with
@app.runtime.torch_jit:
@app.runtime.torch_jitdef _compute_rewards(pole_pos, pole_vel, cart_vel, reset_terminated): ... return rewardOn your machine the decorator returns the function unchanged; on the GPU machine it returns the compiled version. The function can live at module level, where it is easy to read and test.