Skip to main content
A runtime chooses where each rollout’s environment runs. You pass it to task.run / taskset.run at execution time, and the same task and the same env.py run anywhere - only the runtime changes.

Built-in runtimes

Most runtimes are on the top-level package (from hud import LocalRuntime, DockerRuntime, HUDRuntime, HostedRuntime, Runtime); ModalRuntime and DaytonaRuntime import from hud.eval.
You can usually omit runtime=. A run without one uses what HUD already knows:
  • a taskset loaded from Python source (Taskset.from_file / from_module) runs against that source
  • a platform taskset (Taskset.from_api) runs on the platform
  • otherwise, if the envs your tasks name are defined in files you’ve imported, each rollout gets a fresh env served from its file — so my_task().run(agent) just works in the project that defines the env
When none of these apply, run raises and lists the runtimes you can pass — it never silently picks one. If two imported files define an env with the same name, that’s also an error; disambiguate by passing the instance you mean: runtime=LocalRuntime(env).
To deploy an environment to the platform and run against it, see running an eval and deploying to the platform.

RuntimeConfig

RuntimeConfig carries the typed construction input a container-based runtime needs: an image or Compose project, hardware, and timeouts. Set it on the runtime (runtime_config=) or per row on Task.runtime_config; the runtime merges the two and applies what it supports.
Support differs per runtime: DockerRuntime, ModalRuntime, and DaytonaRuntime accept it (Docker ignores limits; Daytona ignores run_timeout_s and resource overrides when booting from a snapshot). LocalRuntime and HUDRuntime reject a per-task runtime_config.

Runtime directory

The constructor for each built-in runtime:

LocalRuntime

Serves a fresh env per rollout, in this process, over the same control channel as every placement. source is any pointer to the env:
  • a .py file or directory that declares it, imported fresh per rollout (sibling imports resolve). env pins one name when the source declares several; it defaults to the placed task’s env.
  • a live Environment declared at module level - its declaring module’s file is the recipe; the instance itself is never served, so every rollout is still fresh.
  • a (task) -> Environment constructor for envs built in code (integrations, parameterized envs), called fresh per rollout with the placed row.
ready_timeout bounds @env.initialize startup. The freshness boundary is the env’s own source: it is re-imported per rollout, while modules it imports follow normal Python import caching and are shared process-wide - state kept in helper modules persists across rollouts. Env hooks run in this process and share its event loop - keep envs async, or use SubprocessRuntime / DockerRuntime when rollouts need whole-process isolation.

SubprocessRuntime

  • path - .py file (or directory) that declares the env. The child’s working directory is the source’s directory, so sibling imports and relative data paths resolve.
  • env - pin a specific env name when the source declares more than one. Defaults to the placed task’s env.
  • ready_timeout - seconds to wait for the child to start serving.

DockerRuntime

  • image - image name to run; shorthand for runtime_config.image.
  • port - port the image’s CMD serves inside the container (the scaffolded Dockerfile.hud serves 8765).
  • run_args - extra docker run flags, e.g. ["--gpus", "all"] or ["-e", "KEY=VAL"].
  • runtime_config - a RuntimeConfig (image or Compose file, resources) for finer control.
For Compose environments, the service named main must serve HUD’s control channel on port. DockerRuntime starts the project and publishes that service’s control port. Constructor-level image is shorthand for runtime_config.image. Project structure, build hooks, service access, and the security override are documented in Compose environments.

ModalRuntime

  • image_name - published Modal image name (the preferred durable handle), e.g. ModalRuntime("hud-libero-env").
  • image - an Image to build lazily on first use, as an escape hatch.
  • command - override the serving command (defaults to the scaffolded hud serve entrypoint).
  • workdir - working directory inside the sandbox. Left unset, Modal keeps the image’s WORKDIR.
  • app_name / port / env_vars - Modal app name, in-sandbox serving port, and extra environment variables.
For Compose input, ModalRuntime runs the project in a Docker-in-Docker sandbox in your Modal account and merges env_vars into main at acquisition time. See Compose environments. Requires the modal extra and a configured token.

DaytonaRuntime

  • snapshot_name - Daytona snapshot to boot from (the durable handle).
  • image - Dockerfile/registry ref to build the snapshot if it’s missing. Daytona records what a snapshot was built from, so when the image content changes (an edited Dockerfile or context file, a repointed registry ref) the snapshot is rebuilt in place under the same name instead of silently reusing the build from before the edit.
  • workdir / port - guest working directory and in-sandbox serving port.
  • ssh_host / ssh_expires_minutes - SSH tunnel settings (Daytona exposes services over an SSH local-forward).
Resources (cpu/memory/gpu) are fixed on the snapshot at build time. With image, a task’s runtime_config.resources builds a sized variant under a suffixed name (my-env-4cpu); without image, an already-built snapshot cannot be resized. For Compose input, Daytona runs main as the rollout sandbox and each sidecar as a linked sandbox. Sidecar images resolve to reusable Daytona snapshots. Compose authoring conventions are documented in Compose environments.

HUDRuntime

  • run_timeout - hard deadline for the full rollout lifecycle. A timeout returns an errored Run with stop_reason="timeout" while teardown completes.
  • runtime_url - override the runtime endpoint the tunnel connects to.
The SDK leases your deployed env by name and tunnels to its control channel; the agent loop runs local.

HostedRuntime

  • poll_interval - seconds between trace-status polls while the rollout runs remotely.
  • run_timeout - hard deadline including submission, provisioning, queueing, and execution. A timeout requests cancellation and returns an errored Run.
Where HUDRuntime runs the agent loop locally against a tunneled env, HostedRuntime runs the whole rollout off-box: the platform leases an instance, brings the env’s container up on it, and runs the agent right next to it. This process only submits the rollout and polls its trace to completion. It supports gateway agents from create_agent; agents with a custom model_client must use HUDRuntime or LocalRuntime.

Runtime

  • url - control-channel address of an already-running substrate (e.g. tcp://host:8765).
  • params - connection-time data a transport may need (auth token, sandbox id).

Shared

  • provider - the Provider to provision once, e.g. DockerRuntime("my-env").
  • width - how many concurrent connections that one substrate accepts.
A lease pool over one substrate: it boots through provider on the first lease and lives for the enclosing async with scope, so every rollout in that scope is handed the same address - agents that need to share, compete, or coordinate in the same environment (a vectorized sim’s num_envs slots, see robots, are one case). Lease width + 1 waits for a slot to free instead of erroring, so group and max_concurrent keep their ordinary meanings. Taskset.run scopes a context-manager placement to the call, so a bare runtime=Shared(...) boots once per run(...); open the scope yourself to keep the substrate warm across several calls:
Runtime(url) needs no wrapping since every caller already dials the same address.

Run on your own infra

A runtime is just a function: given a task, start a container somewhere and yield its control-channel URL. That one function is the whole integration surface for any provider - Modal, E2B, Runloop, your own Kubernetes:
run.py
DockerRuntime and the rest are just built-in versions of this. Anything that starts your image and hands back a URL plugs in with no change to the environment or the task - that’s what “run anywhere” means concretely. Constructed directly, Runtime(url) yields itself with a no-op lifecycle, since whoever provisioned the substrate owns teardown. Placement can also vary per task: a runtime is called once per rollout with the task row being placed, so one callable can route heavier rows to heavier substrates.