OpenEnv documentation

Runtime Providers

Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

Runtime Providers

A runtime provider starts an environment server and returns a base_url that an EnvClient connects to. Container providers implement the same ContainerProvider contract, so switching from local Docker to a cloud sandbox is a one-line change.

Available providers

Provider Backend Install Status
LocalDockerProvider Local Docker daemon core
DockerSwarmProvider Docker Swarm cluster core
UVProvider Local process via uv (no container) core
DaytonaProvider Daytona cloud sandboxes pip install openenv[daytona]
ACASandboxProvider Azure Container Apps Sandboxes pip install openenv[aca]
ModalProvider Modal sandboxes pip install openenv[modal]
KubernetesProvider Kubernetes cluster core 🚧 planned

Cloud-provider SDKs are optional extras, imported lazily, so installing core OpenEnv pulls in no cloud SDK. The core providers (LocalDockerProvider, DockerSwarmProvider, UVProvider) are re-exported from the runtime package; cloud providers are imported from their module:

from openenv.core.containers.runtime import LocalDockerProvider  # core
from openenv.core.containers.runtime.daytona_provider import DaytonaProvider  # cloud

See the Core API reference for each provider’s full API.

Lifecycle

Container providers share the same flow. from_docker_image uses LocalDockerProvider by default; pass provider= to run the server elsewhere:

base_url = provider.start_container(image)
provider.wait_for_ready(base_url, timeout_s=180)
try:
    async with MyEnv(base_url=base_url, provider=provider) as env:
        result = await env.reset()
        ...
finally:
    provider.stop_container()

UVProvider is not a container provider: it runs the server as a local process and exposes .start() / .wait_for_ready() / .stop() instead.

Running many environments in parallel

Scaling out (for example, many concurrent RL rollouts) is a main reason cloud providers exist. The model is one provider and one client per environment: each provider starts its own isolated sandbox, so they run independently. Launch them concurrently with asyncio.gather, wrapping the blocking provider calls in asyncio.to_thread since most cloud SDKs are synchronous:

async def run_one(env_id: int, image) -> str:
    provider = DaytonaProvider()
    base_url = await asyncio.to_thread(provider.start_container, image)
    try:
        await asyncio.to_thread(provider.wait_for_ready, base_url, 300)
        async with MyEnv(base_url=base_url, provider=provider) as env:
            result = await env.reset()
            return result.observation.text
    finally:
        await asyncio.to_thread(provider.stop_container)

image = DaytonaProvider.image_from_dockerfile("envs/echo_env/server/Dockerfile")
results = await asyncio.gather(*(run_one(i, image) for i in range(20)))

Full example: examples/daytona_tbench2_concurrent.py spins up N sandboxes concurrently and reports per-stage timing.

Per-provider setup

ACASandboxProvider

Runs the server in an Azure Container Apps Sandbox. Install with pip install openenv[aca]. Requires Azure credentials (credential=None falls back to DefaultAzureCredential).

from openenv.core.containers.runtime.aca_provider import ACASandboxProvider

provider = ACASandboxProvider(
    subscription_id="<subscription-id>",
    resource_group="<resource-group>",
    sandbox_group="<sandbox-group>",
    region="eastus",   # used to derive the endpoint when endpoint=None
    endpoint=None,
    credential=None,   # defaults to DefaultAzureCredential()
    sdk_kwargs={},
)

DaytonaProvider

Runs the server in a Daytona cloud sandbox. Install with pip install openenv[daytona]. Requires the DAYTONA_API_KEY environment variable.

from openenv.core.containers.runtime.daytona_provider import DaytonaProvider

image = DaytonaProvider.image_from_dockerfile("envs/echo_env/server/Dockerfile")
provider = DaytonaProvider()

Full examples: examples/daytona_tbench2_simple.py and examples/daytona_tbench2_concurrent.py.

DockerSwarmProvider

Deploys the server as a service on a Docker Swarm cluster. Initializes Swarm automatically when it is not already active.

from openenv.core.containers.runtime import DockerSwarmProvider

provider = DockerSwarmProvider()

KubernetesProvider

🚧 Not yet implemented. The class exists as a placeholder for the planned Kubernetes backend.

LocalDockerProvider

Runs the server on the local Docker daemon. This is the default for from_docker_image, so you rarely construct it explicitly.

from openenv.core.containers.runtime import LocalDockerProvider

provider = LocalDockerProvider()

ModalProvider

Runs the server in a Modal sandbox over an encrypted tunnel. Install with pip install openenv[modal]. Requires a configured Modal account (modal setup).

from openenv.core.containers.runtime.modal_provider import ModalProvider

image = ModalProvider.image_from_dockerfile("envs/echo_env/server/Dockerfile")
provider = ModalProvider(app_name="openenv")

Full example: examples/modal_echo_env.py.

UVProvider

Runs the server as a local process via uv, without a container. Useful for developing an environment from a checkout.

from openenv.core.containers.runtime import UVProvider

provider = UVProvider(project_path="path/to/env")
base_url = provider.start()
provider.wait_for_ready()
Update on GitHub