> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.nvidia.com/nemo/gym/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.nvidia.com/nemo/gym/_mcp/server.

# nemo_gym.sandbox.providers.enroot

Enroot provider package.

## Submodules

* **[`nemo_gym.sandbox.providers.enroot.provider`](/nemo/gym/nemo-gym/nemo_gym/sandbox/providers/enroot/provider)**

## Package Contents

### Classes

| Name                                                                                                         | Description                                                                  |
| ------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------- |
| [`EnrootCreateConfig`](#nemo_gym-sandbox-providers-enroot-provider-EnrootCreateConfig)                       | Settings for creating an Enroot sandbox container.                           |
| [`EnrootCreateError`](#nemo_gym-sandbox-providers-enroot-provider-EnrootCreateError)                         | Raised when Enroot cannot create a sandbox.                                  |
| [`EnrootCreateVerificationError`](#nemo_gym-sandbox-providers-enroot-provider-EnrootCreateVerificationError) | Raised when a newly-created sandbox cannot execute a probe command.          |
| [`EnrootExecConfig`](#nemo_gym-sandbox-providers-enroot-provider-EnrootExecConfig)                           | Settings for running commands inside an Enroot sandbox.                      |
| [`EnrootProbeConfig`](#nemo_gym-sandbox-providers-enroot-provider-EnrootProbeConfig)                         | Post-create probe settings: a test command confirming the sandbox is usable. |
| [`EnrootProvider`](#nemo_gym-sandbox-providers-enroot-provider-EnrootProvider)                               | Sandbox provider backed by the local Enroot CLI.                             |

### API

```python
class nemo_gym.sandbox.providers.enroot.EnrootCreateConfig(
    mount_point: str = DEFAULT_MOUNT_POINT,
    base_dir: str | None = None,
    data_path: str | None = None,
    cache_path: str | None = None,
    runtime_path: str | None = None,
    sqsh_cache_dir: str | None = None,
    rw: bool = True,
    remap_root: bool = False,
    bypass_entrypoint: bool = True,
    init_command: str = DEFAULT_INIT_COMMAND,
    import_timeout_s: float | None = 1800,
    create_timeout_s: float | None = 600,
    start_timeout_s: float | None = 600,
    start_poll_s: float = 0.5,
    extra_import_args: list[str] = list(),
    extra_create_args: list[str] = list(),
    extra_start_args: list[str] = list()
)
```

Dataclass

Settings for creating an Enroot sandbox container.

**`base_dir`** `str | None = None`

---

**`bypass_entrypoint`** `bool = True`

---

**`cache_path`** `str | None = None`

---

**`create_timeout_s`** `float | None = 600`

---

**`data_path`** `str | None = None`

---

**`extra_create_args`** `list[str] = field(default_factory=list)`

---

**`extra_import_args`** `list[str] = field(default_factory=list)`

---

**`extra_start_args`** `list[str] = field(default_factory=list)`

---

**`import_timeout_s`** `float | None = 1800`

---

**`init_command`** `str = DEFAULT_INIT_COMMAND`

---

**`mount_point`** `str = DEFAULT_MOUNT_POINT`

---

**`remap_root`** `bool = False`

---

**`runtime_path`** `str | None = None`

---

**`rw`** `bool = True`

---

**`sqsh_cache_dir`** `str | None = None`

---

**`start_poll_s`** `float = 0.5`

---

**`start_timeout_s`** `float | None = 600`

---

```python
nemo_gym.sandbox.providers.enroot.EnrootCreateConfig.__post_init__() -> None
```

```python
class nemo_gym.sandbox.providers.enroot.EnrootCreateError()
```

**Bases:** [SandboxCreateError](/nemo/gym/nemo-gym/nemo_gym/sandbox/providers/base#nemo_gym-sandbox-providers-base-SandboxCreateError)

Raised when Enroot cannot create a sandbox.

```python
class nemo_gym.sandbox.providers.enroot.EnrootCreateVerificationError()
```

**Bases:** [SandboxCreateVerificationError](/nemo/gym/nemo-gym/nemo_gym/sandbox/providers/base#nemo_gym-sandbox-providers-base-SandboxCreateVerificationError)

Raised when a newly-created sandbox cannot execute a probe command.

```python
class nemo_gym.sandbox.providers.enroot.EnrootExecConfig(
    default_timeout_s: float | None = 180,
    default_mounts: list[str] = list(),
    extra_exec_args: list[str] = list(),
    concurrency: int = 32,
    exec_shell: str = 'sh'
)
```

Dataclass

Settings for running commands inside an Enroot sandbox.

**`concurrency`** `int = 32`

---

**`default_mounts`** `list[str] = field(default_factory=list)`

---

**`default_timeout_s`** `float | None = 180`

---

**`exec_shell`** `str = 'sh'`

---

**`extra_exec_args`** `list[str] = field(default_factory=list)`

---

```python
nemo_gym.sandbox.providers.enroot.EnrootExecConfig.__post_init__() -> None
```

```python
class nemo_gym.sandbox.providers.enroot.EnrootProbeConfig(
    command: str | None = READY_PROBE_COMMAND,
    expected_stdout: str | None = READY_PROBE_EXPECTED,
    timeout_s: int = 30,
    deadline_s: float | None = None,
    stable_count: int = 1,
    stable_delay_s: float = 0.0
)
```

Dataclass

Post-create probe settings: a test command confirming the sandbox is usable.

**`command`** `str | None = READY_PROBE_COMMAND`

---

**`deadline_s`** `float | None = None`

---

**`expected_stdout`** `str | None = READY_PROBE_EXPECTED`

---

**`stable_count`** `int = 1`

---

**`stable_delay_s`** `float = 0.0`

---

**`timeout_s`** `int = 30`

---

```python
nemo_gym.sandbox.providers.enroot.EnrootProbeConfig.__post_init__() -> None
```

```python
class nemo_gym.sandbox.providers.enroot.EnrootProvider(
    exec: nemo_gym.sandbox.providers.enroot.provider.EnrootExecConfig | collections.abc.Mapping[str, typing.Any] | None = None,
    create: nemo_gym.sandbox.providers.enroot.provider.EnrootCreateConfig | collections.abc.Mapping[str, typing.Any] | None = None,
    probe: nemo_gym.sandbox.providers.enroot.provider.EnrootProbeConfig | collections.abc.Mapping[str, typing.Any] | None = None
)
```

Sandbox provider backed by the local Enroot CLI.

**`_binary`** `= _require_enroot()`

---

**`_create_config`** `= _coerce_config(create, EnrootCreateConfig)`

---

**`_enroot_env`**

---

**`_exec_config`** `= _coerce_config(exec, EnrootExecConfig)`

---

**`_import_locks`** `dict[str, Lock] = {}`

---

**`_probe`** `= _coerce_config(probe, EnrootProbeConfig)`

---

**`_semaphore`** `= asyncio.Semaphore(self._exec_config.concurrency)`

---

**`_sqsh_cache_dir`** `= Path(cfg.sqsh_cache_dir or base / 'sqsh')`

---

**`name`** `= 'enroot'`

---

```python
nemo_gym.sandbox.providers.enroot.EnrootProvider._await_container_pid(
    instance: nemo_gym.sandbox.providers.enroot.provider._EnrootInstance,
    err_f: typing.IO[bytes]
) -> int
```

async

Poll `enroot list` until the container's init PID appears, or raise.

```python
nemo_gym.sandbox.providers.enroot.EnrootProvider._cleanup_failed_create_handle(
    handle: nemo_gym.sandbox.providers.base.SandboxHandle
) -> None
```

async

Best-effort teardown of a sandbox that failed to start or verify.

```python
nemo_gym.sandbox.providers.enroot.EnrootProvider._ensure_image(
    image: str
) -> pathlib.Path
```

async

Return a local squashfs path for `image`, importing (and caching) if needed.

```python
nemo_gym.sandbox.providers.enroot.EnrootProvider._kill_start_group(
    instance: nemo_gym.sandbox.providers.enroot.provider._EnrootInstance
) -> None
```

Best-effort SIGTERM then SIGKILL of the detached start's process group.

```python
nemo_gym.sandbox.providers.enroot.EnrootProvider._lookup_container(
    name: str
) -> tuple[int | None, bool]
```

async

Return (pid, present). pid is the running init PID or None; present is
whether the rootfs exists at all (running or not).

```python
nemo_gym.sandbox.providers.enroot.EnrootProvider._pid_has_container_marker(
    pid: int,
    name: str
) -> bool
```

staticmethod

Return True if /proc/\<pid>/cmdline still carries the container's unique marker.

This guards against PID reuse: if the init exits and Linux reuses the PID
for an unrelated process, the container marker won't appear in the new
process's cmdline, so exec() returns a sandbox error instead of joining
the wrong namespace.

```python
nemo_gym.sandbox.providers.enroot.EnrootProvider._read_temp(
    handle: typing.IO[bytes]
) -> str
```

staticmethod

```python
nemo_gym.sandbox.providers.enroot.EnrootProvider._resolve_image(
    image: str
) -> tuple[str | None, pathlib.Path | None]
```

Return (import\_uri, sqsh\_path). Exactly one is non-None.

A local `.sqsh` file is used directly (no import). Anything else is
translated to an enroot import URI.

```python
nemo_gym.sandbox.providers.enroot.EnrootProvider._run(
    argv: list[str],
    timeout_s: float | None,
    stdin: bytes | None = None
) -> tuple[int, str, str]
```

async

Run an enroot CLI command. Returns (return\_code, stdout, stderr).

Enforces timeout via asyncio.wait\_for and kills the whole process group
on timeout so child processes do not linger. Bounds concurrency with a
shared semaphore. Decodes output with errors="replace". Every call runs
with the pinned ENROOT\_\* environment.

```python
nemo_gym.sandbox.providers.enroot.EnrootProvider._start_detached(
    argv: list[str]
) -> tuple[typing.Any, typing.IO[bytes], typing.IO[bytes]]
```

async

Launch the long-lived `enroot start` init without awaiting its exit.

`enroot start` does not daemonize — it stays in the foreground for the
whole container lifetime. We launch it detached in its own session
(start\_new\_session=True), capture output to temp files (so an early exit
leaves diagnosable stderr), and return the process handle plus the temp
files. The caller confirms readiness by polling `enroot list`.

```python
nemo_gym.sandbox.providers.enroot.EnrootProvider._verify_created_handle(
    handle: nemo_gym.sandbox.providers.base.SandboxHandle
) -> None
```

async

Run the readiness probe until the sandbox responds, or raise.

* probe.command is None      -> skip (no verification).
* probe.deadline\_s is None   -> single attempt; a failure raises immediately.
* probe.deadline\_s is set    -> poll until the sandbox passes the probe
  `stable_count` consecutive times, or the deadline elapses.

```python
nemo_gym.sandbox.providers.enroot.EnrootProvider.aclose() -> None
```

async

No provider-wide resources to close.

```python
nemo_gym.sandbox.providers.enroot.EnrootProvider.close(
    handle: nemo_gym.sandbox.providers.base.SandboxHandle
) -> None
```

async

Kill the container init, remove the rootfs, and clean up the staging dir.

```python
nemo_gym.sandbox.providers.enroot.EnrootProvider.create(
    spec: nemo_gym.sandbox.providers.base.SandboxSpec
) -> nemo_gym.sandbox.providers.base.SandboxHandle
```

async

Import/create the rootfs, launch a detached init, and return a ready handle.

```python
nemo_gym.sandbox.providers.enroot.EnrootProvider.download_file(
    handle: nemo_gym.sandbox.providers.base.SandboxHandle,
    source_path: str,
    target_path: pathlib.Path
) -> None
```

async

Download one sandbox file to the host.

Fast path (source under the bind mount): read directly from the host side
of the shared folder. Fallback (arbitrary path): cp inside the container
into the shared folder, then read the host side.

```python
nemo_gym.sandbox.providers.enroot.EnrootProvider.exec(
    handle: nemo_gym.sandbox.providers.base.SandboxHandle,
    command: str,
    cwd: str | None = None,
    env: dict[str, str] | None = None,
    timeout_s: int | float | None = None,
    user: str | int | None = None,
    stdin: bytes | None = None
) -> nemo_gym.sandbox.providers.base.SandboxExecResult
```

async

Run a command inside the container via `enroot exec &lt;pid&gt;`.

Maps the neutral `user` parameter onto enroot:

* None            -> run as the default (launching) user, or root if the
  container was started with remap\_root.
* "root" / 0      -> run directly (requires create.remap\_root=true to be
  root inside the container).
* other user/uid  -> wrap in `su` to switch to that user (requires root
  inside the container).

`stdin`, when given, is piped to the command's standard input.

```python
nemo_gym.sandbox.providers.enroot.EnrootProvider.status(
    handle: nemo_gym.sandbox.providers.base.SandboxHandle
) -> nemo_gym.sandbox.providers.base.SandboxStatus
```

async

Return the container's lifecycle status via `enroot list -f`.

Liveness is keyed off the PID column, not name presence: enroot lists
created-but-not-running rootfs with an empty PID. Running -> RUNNING;
present without a PID, or absent -> STOPPED; timeout/parse error -> UNKNOWN.

```python
nemo_gym.sandbox.providers.enroot.EnrootProvider.upload_file(
    handle: nemo_gym.sandbox.providers.base.SandboxHandle,
    source_path: pathlib.Path,
    target_path: str
) -> None
```

async

Upload one host file into the sandbox.

Fast path (target under the bind mount): write directly to the host side
of the shared folder. Fallback (arbitrary path): stage into the shared
folder, then cp inside the container.