Skip to main content
Apple’s 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 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:
--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.
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.
Start a container from that image:
Create sandbox.yaml with your backends and mount paths, following The workspace inside the sandbox. Then copy it into the container and start the workspace:
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 sets up a host.container.internal name for sandbox-side configs.

Connect from TypeScript

The container CLI is the transport, so there is no SDK dependency. 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:
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). 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.
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.
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). See Resource limits.