Skip to main content
A workspace config describes one virtual terminal. Python and TypeScript use snake_case YAML keys. TypeScript constructor options use camelCase, such as commandLimits; provider and runtime guides show their SDK equivalents.

Start with RAM

mounts is the only required top-level key. This config creates a writable, in-memory mount without credentials:
workspace.yaml
Create a workspace, check it, and remove it:
The shell command prints ready. Replace RAM using a VFS setup guide. YAML defaults mode to write; direct Python and TypeScript Workspace constructors default to read.

From a template

A config saved on the server as a template creates workspaces without the file. The template’s config is the same document as workspace.yaml:
Each workspace gets a copy of the config, so editing the template later does not change it. See From a template.

Workspace settings

Unknown keys are errors. Provider fields belong under mounts.<prefix>.config, CLI fields under clis.<name>.config, and a machine’s fields beside its provider under machines.<name>.

Mount settings

Each mount key is an absolute virtual prefix, such as /data. backend takes one of these values:

Network

network decides where the workspace can connect. It covers every connection a line or a file call opens, from a command, a CLI, a runtime or a mount. It does not cover a program on a machine: that program connects from the machine under the machine’s own network settings, and network, pre_network and routes do not apply to it.
Each list entry is one of:
  • *, for every host.
  • A host name, such as api.github.com.
  • *. and a name, such as *.slack.com, for every host under that name.
  • An address, or a range in CIDR form, such as 10.0.0.0/8.
Any entry except a range can end in :port to match only that port. An IPv6 address puts its port after brackets: [::1]:8080. A name matches the host the connection was opened for. An address or a range matches the address the connection reaches. A denied entry wins over an allowed one, and an empty allowed_domains lets nothing through. Write false, not off: only some YAML readers take off as false. Related settings:
  • Mounts connect under the same rules, so the lists must let each mount’s endpoint through.
  • A profile can hold its sessions to a narrower network.
  • routes sends a connection for one host:port to another. See Route Network.
  • Policies can also check each connection the lists let through, and cap how much it carries, at pre_network.

When a connection is refused

A refused connection fails the way a closed port does: curl exits 7 with Could not connect to server.

In a browser or on an edge runtime

fetch and WebSocket are checked by host name only. Mirage never sees the address a name resolves to, so a workspace whose denied_domains names an address or a range is refused. On an edge runtime, a limited workspace checks each redirect under the same rules. In a browser, it follows no redirects, because a page cannot see where a redirect goes.

Host machines

A host machine runs real programs on the host, the computer or server where Mirage runs, sandboxed or not. Those programs open their own connections, so Mirage cannot see or block them. While network limits anything, or a policy checks connections, a host machine refuses to run them: Permission denied, exit 126.

System time

system_time sets the time the workspace starts at, as an ISO 8601 time with a zone offset, from 1970 through 2261. From there it moves forward in real time:
Right after it starts, date -u prints Mon Mar 2 09:00:00 UTC 2026, a file written then is stamped 09:00 UTC, and Python’s datetime.date.today() is 2026-03-02. date, file times, ls -l, find -mtime, jq’s now and git commits all see this time, and so do the Monty, WASI, Pyodide and QuickJS runtimes. TZ in env picks the zone date shows it in. With no TZ a workspace runs on UTC, whatever zone the server is in. Without system_time, the workspace uses the server’s clock. Each workspace keeps its own time, so workspaces on one server can each start at a different date. Snapshots keep the setting, and GET /v1/workspaces/{id} returns it. Programs a host machine runs, sandboxed or not, see the time through libfaketime when it is installed, on Linux (apt install libfaketime). Without it, or on macOS, they run on the host’s clock. A statically linked program, as many Go tools are, also reads the host’s clock. Programs on a machine work the same way, with libfaketime in the machine image and the machine’s clock without it.

CLI entries

Each clis key is the command name the CLI installs under. An entry takes exactly one of cli or script, and always a machine:
A script entry’s config cannot hold a credential pointer. Read the credential from an env pointer instead.

Workspace state and caches

These keys set where a workspace keeps its own data and its copies of what it reads. See the Infrastructure pages for each one in YAML, Python and TypeScript. A cache or index block needs a type. A store block without one keeps in memory every part that its namespace, sessions, logs or execution key does not move elsewhere. A redis type defaults url to redis://localhost:6379/0.

Values and paths

${NAME} placeholders are substituted from the host process environment when the config loads. NAME is uppercase letters, digits and underscores, not starting with a digit; any other ${...} is left as written. A missing variable is an error. Use a credential pointer when the value should be fetched through a source instead. A config posted to a server is taken as written: mirage workspace create substitutes from your shell first, and the server never substitutes from its own environment. Relative script paths and file-based code references resolve from the config file’s directory. Relative dotenv paths resolve from the Mirage process’s working directory.

Sizes

A size is a number of bytes, optionally followed by K, M or G and an optional B, in any case. The suffixes are binary: K is 1024 bytes. Anything else is refused with an error naming the key, such as 1.5GB, 100 MB, 100MiB or 1TB. These keys take a size:
workspace.yaml
So do the server’s ceilings, in the [daemon] section of ~/.mirage/config.toml (max_memory = "64MB") or as environment variables (MIRAGE_MAX_MEMORY=64MB).