> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mirage.strukto.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Docker

> Run captured programs in a Docker container you started.

The `docker` machine provider reaches a running container, by name, and the
`machine` runtime runs captured programs in it. You start
and stop the container; Mirage only runs programs in it. Python SDK and
Node.

## Installation

> [tab] Python
>
> ```bash theme={null}
> pip install 'mirage-ai[ssh]'
> ```

> [tab] TypeScript
>
> ```bash theme={null}
> pnpm add ssh2
> ```

These serve the workspace into the container with `mount_type: ssh`. Start
the container with FUSE allowed, from an image that has what
[connecting the workspace](#connecting-the-workspace) needs:

```bash theme={null}
docker run -d --cap-add SYS_ADMIN --device /dev/fuse \
  --name my-sandbox my-image sleep infinity
```

## Usage

`captures` defaults to [`@external`](https://docs.mirage.strukto.ai/home/setup/runtime/overview#captures):
every program Mirage does not have itself. Name `@python` to run
Python in the container too.

> [tab] Python
>
> ```python theme={null}
> from mirage import RAMVFS, MountMode, Workspace
> from mirage.runtime.machine import MachineRuntime
>
> ws = Workspace(
>     {"/data": RAMVFS()},
>     mode=MountMode.EXEC,
>     machines={
>         "box": {
>             "provider": "docker",
>             "container": "my-sandbox",
>             "mount_type": "ssh",
>         }
>     },
>     runtimes=[MachineRuntime(["@python", "@external"], machine="box")],
> )
> await ws.shell("python3 -c 'print(1 + 1)'")
> ```

> [tab] TypeScript
>
> ```typescript theme={null}
> import { MountMode, MachineRuntime, RAMVFS, Workspace } from '@struktoai/mirage-node'
>
> const ws = new Workspace(
>   { '/data': new RAMVFS() },
>   {
>     mode: MountMode.EXEC,
>     machines: { box: { provider: 'docker', container: 'my-sandbox', mountType: 'ssh' } },
>     runtimes: [new MachineRuntime({ captures: ['@python', '@external'], machine: 'box' })],
>   },
> )
> await ws.shell("python3 -c 'print(1 + 1)'")
> ```

> [tab] YAML
>
> ```yaml theme={null}
> mode: exec
> machines:
>   box:
>     provider: docker
>     container: my-sandbox
>     mount_type: ssh
> runtimes:
>   - name: machine
>     captures: ["@python", "@external"]
>     machine: box
> mounts:
>   /data:
>     vfs: ram
> ```

## Connecting the workspace

A program in the container opens workspace files at the same paths as in
Mirage, so the container must see the mounts there. `mount_type` picks how:

| `mount_type` | How the files get there | Rules that apply |
| - | - | - |
| `ssh` | Mirage serves every mount over `docker exec`; the container mounts each with `sshfs` | This workspace's mount modes, hides, permissions and policies |
| `fuse` | A Mirage inside the container mounts your backends with FUSE | That Mirage's own config |
| `both` | The container's own FUSE mounts where it has them, `ssh` for the rest | Each side's own |
| unset | Nothing; you put the files there yourself | The container's |

### ssh

* The image needs only `sshfs`. No Mirage, keys or open port in the
  container.
* Each file call is a round trip to the workspace; with `both`, keep bulk
  data on the container's own mounts.
* The container shows one session's view, the first that runs a line there.
  Give each agent its own container.
* An sshfs still serving a path Mirage serves, another workspace's or one
  of your own, is left alone and the line exits 1. Give each workspace its
  own container.
* If the container restarts, Mirage mounts the paths again. A line caught
  mid-restart exits 126 without running.
* The root mount and `/dev` are not served.

### fuse

Build [the machine image](https://docs.mirage.strukto.ai/home/setup/machine/overview.md#the-machine-image), which
carries Mirage and `fuse3`. Write the same mounts at the same paths into
`sandbox.yaml`, and start Mirage with it in the container:

```yaml theme={null}
mounts:
  /data:
    vfs: s3
    config:
      bucket: my-bucket
    backend: fuse
    mountpoint: /data
```

```bash theme={null}
docker cp sandbox.yaml my-sandbox:/tmp/sandbox.yaml
docker exec my-sandbox mirage workspace create /tmp/sandbox.yaml
```

* This workspace's rules do not reach those files, so a line is refused
  while the session's permissions hide or narrow anything under them.
* If a path the container mounted goes away, as on a restart, lines exit
  126 until it is mounted again. Start Mirage from the image's entrypoint
  so a restart mounts it again.
* Keep the container's paths in step with the host's mounts yourself.
  `sandbox.yaml` can differ where it should, such as an endpoint
  (`host.docker.internal`) or narrower credentials.

## Runtime behavior

| Capability | In the container |
| - | - |
| Command | The captured program and its words, as typed |
| Piped input | stdin |
| Environment | The session's, over the config `env` |
| Working directory | The shell's, unchanged |
| Workspace files | By `mount_type`, above |
| Disk and network | The container's own |

## Notes

* `container` takes a name or an ID. The Docker CLI is the transport, so
  Docker Desktop, Colima and a compatible Podman alias all work.
* Sizing, GPUs, networks, volumes and bind mounts go on your own
  `docker run`. You can bake `sandbox.yaml` into the image instead of
  copying it.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.