> ## 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.

# Apple Container

> Run captured command lines in an existing container under Apple's container tool.

Apple's [`container`](https://github.com/apple/container) runs every Linux
container in its own lightweight VM with its own kernel, on Apple silicon with
macOS 26 or later. `AppleContainerRuntime` connects to a container you
started and executes every captured line inside it; Mirage never starts,
stops, or deletes one. See the [Sandbox overview](/python/runtime/sandbox)
first for routing, the shared image, and how to serve workspace mounts inside
the sandbox.

## Start the container

Install the tool and start its services once, then build the sandbox image
from the Mirage repository root:

```bash theme={null}
brew install container
container system start
container build -f docker/sandbox/Dockerfile --target fuse \
  -t mirage-python-fuse .
```

`--target fuse` selects the Dockerfile's FUSE build stage. `-t mirage-python-fuse`
assigns a local name to the resulting image, which contains Python, Mirage,
and FUSE (Filesystem in Userspace). FUSE makes workspace backends accessible
as ordinary files inside the container.

<Note>
  This example reuses Mirage's shared Python image to serve the workspace
  mounts. You can control it from the TypeScript SDK: the host SDK and the
  Mirage implementation inside the container are independent. For a custom
  image, see [The sandbox image](/python/runtime/sandbox#the-sandbox-image).
</Note>

Start a container from that image:

```bash theme={null}
container run -d --cap-add SYS_ADMIN \
  --name my-sandbox mirage-python-fuse sleep infinity
```

| Part of `container run` | Meaning |
| - | - |
| `-d` | Run the container in the background. |
| `--cap-add SYS_ADMIN` | Allow FUSE mounts; Apple Container already provides `/dev/fuse`. |
| `--name my-sandbox` | Give this container a name; use it in the runtime's `container` config. |
| `mirage-python-fuse` | Use the image built above. |
| `sleep infinity` | Keep the container running while Mirage sends commands through `container exec`. |

Create `sandbox.yaml` with your backends and mount paths, following
[The workspace inside the sandbox](/python/runtime/sandbox#the-workspace-inside-the-sandbox).
Then copy it into the container and start the workspace:

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

The last command starts Mirage inside the container and FUSE-mounts your
backends at the configured paths, so programs there can read and write your
workspace files.

Host directories reach the container only if you pass them at start with
`--volume`; the guest otherwise sees nothing from the host filesystem. A
service on your Mac is not at `127.0.0.1` inside the container either; Apple's
[host integration guide](https://github.com/apple/container/blob/main/docs/host-integration.md)
sets up a `host.container.internal` name for sandbox-side configs.

## Connect from Python

```python theme={null}
from mirage import MountMode, Workspace
from mirage.runtime.sandbox.apple_container import AppleContainerRuntime

runtime = AppleContainerRuntime(captures=["python3"],
                                config={"container": "my-sandbox"})
ws = Workspace({"/data": ...}, mode=MountMode.EXEC,
               runtimes=[runtime, "workspace"])
await ws.shell("python3 job.py", cwd="/data")
```

The `container` CLI is the transport, so there is no SDK dependency or Mirage
extra. The `container` config is the id the container was started with (its
`--name`); unknown config fields fail at construction. The first captured line
checks `container inspect` and accepts only `running`; a `stopped` container
needs `container start`. Stdin and stderr stay separate.

## Differences from `docker exec`

Every captured command runs under a short `sh` prelude, so the image needs a
POSIX `sh`. The prelude evens out two ways `container exec` differs from
`docker exec`:

* `container exec -e` appends to the image's environment rather than
  replacing a variable the image already sets, so a program started directly
  would read the image's `PATH`. Under the prelude the session's value wins,
  as it does with Docker.
* `container exec -w` creates a working directory that does not exist. Under
  the prelude, a cwd the container does not serve fails loudly
  (`can't cd to /data`) instead of running in an empty directory whose writes
  never reach the backend.

## One container per agent

A line runs in its session's container. List agents under `containers`,
keyed by the session id each agent's session is created with; a session not
listed runs in `container`:

```python theme={null}
runtime = AppleContainerRuntime(captures=["python3"], config={
    "container": "my-sandbox",
    "containers": {"agent_a": "sandbox-a", "agent_b": "sandbox-b"},
})
agent_a = await ws.session("agent_a", profile="guarded")
await agent_a.shell("python3 job.py", cwd="/data")  # runs in sandbox-a
```

In YAML the same block is
`config: {container: my-sandbox, containers: {agent_a: sandbox-a, agent_b: sandbox-b}}`.
Separate containers are separate
VMs, so agents share no files, processes, CPU or memory. Each container serves
its own view of the workspace: with Mirage inside it, make that agent's rules
the `default` profile in its `sandbox.yaml`; with a mount over SFTP, bind the
container's key to the agent's profile
([Bind a key to a profile](/home/access/ssh#bind-a-key-to-a-profile)). A session
with no container of its own and no `container` fails with
`apple_container has no container for session ...`. Each container is checked
with `container inspect` on its first line.

<Note>
  One container is one VM. Concurrent lines sent to the same container share
  its filesystem and process table, so give agents that must not see each
  other their own container.
</Note>

<Warning>
  Mirage timeouts stop waiting for the command; they do not kill the process
  inside the container. `container exec` is meant to forward signals into the
  guest, but release 1.4.1 fails to deliver them
  ([apple/container#1941](https://github.com/apple/container/issues/1941)).
  See [Resource limits](/python/runtime/sandbox#resource-limits).
</Warning>


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