# minimal.toml

> Schema reference for the minimal.toml configuration file: upstream, stack, defaults, tasks, and outputs sections.

Source: https://minimal.dev/docs/reference/minimal-dot-toml
Last updated: 2026-10-11

The `minimal.toml` file defines the configuration for Minimal in a codebase.

This file must be present at the base of the repository (i.e. `./minimal.toml`), or
in a `.minimal` directory at the base of the repository.

Unless [`-C`](https://minimal.dev/docs/reference/cli-mip#global-flags) is specified, the [mip CLI](https://minimal.dev/docs/reference/cli-mip) searches the directory tree backwards from
the current directory till a `minimal.toml` file is found. This behavior allows minimal to be invoked in project directories.

## Example

```toml
[upstream]
repo = "https://github.com/gominimal/pkgs"
branch = "main"
locked_commit = "d39aaaa581f983d6b3ba5eaaf383485a602f37f0"

[stack]
use = "pnpm"
build_packages = ["railway"]

[defaults]
state_key = "dev"

[session]
packages = ["base", "git", "nano"]

[tasks.dev]
exec = "pnpm run dev"

[tasks.preview]
env_vars.PORT = "8080"
bash = "pnpm run build && pnpm run start"

[tasks.deploy]
packages = ["railway"]
exec = "railway up"

[tasks.shell]
interactive = true
packages = ["base", "git", "nano"]
exec = "bash -l"
```

## Schema

### `[upstream]` - Where software comes from {#upstream}

The `[upstream]` section defines the precise source of packages & stacks. This represents the
preceding link in the [software supply chain](https://minimal.dev/docs/concepts/software-supply-chain).

```toml
[upstream]
repo = "<git URL>"
branch = "<branch>"
locked_commit = "<commit hash>"
```

`locked_commit` is automatically updated when [`mip update`](https://minimal.dev/docs/reference/cli-mip) is
run: it re-resolves `branch` to its current HEAD and rewrites the
`locked_commit` of the upstream (and of every sideload) in `minimal.toml` in
place; expect a diff on these fields after running it.

#### `[[upstream.sideload]]` - Additional software sideloaded into your supply chain {#sideload}

Sideload entries let you load in additional packages or stacks from a separate repository, but those packages
are built using the version of packages from your upstream.

Each sideload entry is loaded in order from the specified repository, and follows the same schema as `[upstream]` (the canonical table name is `sideloads`; `sideload` is an accepted alias):

```toml
[[upstream.sideload]]
repo = "<git URL>"
branch = "<branch>"
locked_commit = "<commit hash>" # Updated via `mip update`
```

Sideload repositories have the same layout as an upstream: that is having a `minimal.toml` file, and `packages/` / `stacks/`
directories as needed.

### `[stack]` - How to build code in your repo {#stack}

```toml
[stack]
use = "<stack name>"
build_packages = ["<additional build package>"]     # optional
runtime_packages = ["<additional runtime package>"] # optional
```

The `[stack]` section configures the [stack](https://minimal.dev/docs/concepts/stacks) to use for building code, if any.
See [stack specs](https://minimal.dev/docs/reference/stack-specs) for how stacks themselves are defined.

`use` is an accepted alias for the canonical `name` key, and both parse.
`[harness]`, the name of this section before v0.5.0, is no longer accepted:
a file that still uses it fails to load, naming the rename, so rename the
section to `[stack]`.

The environment variables and packages configured on a stack are inherited on all tasks in this repository.

`build_packages` and `runtime_packages` are optional fields that allow you to declare
additional package dependencies for build time or run time respectively.

### `[defaults]` - Settings for all tasks {#defaults}

```toml
[defaults]
state_key = "<state key>"  # optional
```

When set, `defaults.state_key` will set a state key on all tasks which do not set `state_key`.

### `[session]` - What every contributor's session gets {#session}

Contributes to [sessions](https://minimal.dev/docs/concepts/sessions) activated
on this project (`min session activate`). It carries the same primitives as a
[loadout](https://minimal.dev/docs/reference/loadouts) (packages, vars, patches, lifecycle hooks), scoped to
the project rather than the developer: where a loadout says "what this
developer wants everywhere", the session block says "what every session
working on this codebase needs". There is at most one per `minimal.toml`, and
it has no name or description.

```toml
[session]
# Toolchain every contributor's session gets.
packages = ["rustc", "cargo", "postgresql-client"]

# Config that lives in the repo, patched into the session's home.
patches = [
    { dest = ".cargo/config.toml", source = "config/cargo.toml" },
    { dest = ".psqlrc",            source = "config/psqlrc" },
]

[session.vars]
# Applies uniformly to every developer's session.
RUST_LOG         = "info"
CARGO_TERM_COLOR = "always"
# Inherit from the developer's environment if set, else the default.
DATABASE_URL     = { inherit = true, default = "postgres://localhost/dev" }
# Inherit with no fallback: a developer who has not set this cannot
# activate a session on this project.
GITHUB_TOKEN     = { inherit = true }

# Declared to warm the compile cache when a session comes up.
[[session.lifecycle_hooks]]
on_activate = { type = "inline", value = "cargo check --workspace >/dev/null 2>&1 || true" }
```

- **`packages`**: Packages brought into every session on this project,
  alongside whatever the developer's loadouts contribute.
- **`vars`**: Environment variables. A string sets a fixed value;
  `{ inherit = true }` passes the developer's own value through. Here that
  inheritance is **required**: if the developer's environment does not have
  the variable set, activation fails rather than proceeding without it.

  ```console
  $ min session activate
  error: Composition gating failed: could not resolve pending var
  `GITHUB_TOKEN`: environment variable not found
  ```

  Adding `default = "..."` supplies a fallback and makes the variable
  optional again. A loadout spells the bare form the same way but treats it
  as optional — see
  [Where `{ inherit = true }` differs](#inherit-divergence).

  One name is read specially: setting `SHELL` to a supported, installed
  shell makes the session's attach shell that shell instead of bash. See
  [`SHELL` — Which shell an attach starts](https://minimal.dev/docs/reference/loadouts#session-shell) for
  the supported shells and the fallback behavior.
- **`patches`**: `{ source, dest }` rows copying files into the session.
  `dest` is relative to the session user's home directory; `source` resolves
  on the host, typically inside the repo. The one expansion a loadout has
  and a project does not is `$LOADOUT_ROOT`, which names a loadout's own
  directory; referencing it here fails the activation.
- **`lifecycle_hooks`**: Scripts declared for session transition points
  (`on_activate`, `on_destroy`, `on_attach`, `on_detach`), run inside the
  session under POSIX `sh` — or under whatever a leading shebang names, for
  a hook you would rather write in fish or Python. A project's hooks require
  the developer to allow-list the project in their
  [user policy](https://minimal.dev/docs/reference/user-policy) before they will run — a hook is arbitrary
  code, so the developer must opt in.

The field shapes and composition semantics (conflicts, policy gating) are the
same as loadouts; see the [loadout reference](https://minimal.dev/docs/reference/loadouts) for the exact
rules. Variable *resolution* is the exception: a bare `{ inherit = true }`
that the host has not set fails activation here, where a loadout drops it
with a warning, so the loadout page's rule for that one case does not carry
over.

#### Where `{ inherit = true }` differs {#inherit-divergence}

Three surfaces accept `{ inherit = true }`, spelled identically on each.
They agree that a variable the host *has* set is passed through, and they
disagree about everything else:

| Surface | Host variable unset | `default = "..."` |
|---|---|---|
| This file's `[session.vars]` (and `[[session.vars_lenient]]`) | Activation fails: `could not resolve pending var <NAME>` | Supported; the default is used |
| A loadout's [`[vars]`](https://minimal.dev/docs/reference/loadouts#vars) and [`[[vars_lenient]]`](https://minimal.dev/docs/reference/loadouts#vars_lenient) | Dropped, with a warning on the terminal; activation continues | Supported; the default is used |
| A task's [`env_vars`](https://minimal.dev/docs/reference/tasks#env_vars) | The run fails before the session is built | Rejected when the file is parsed — a task's `inherit` takes no `default` |

Two things narrow the task row. An `echo` task never builds an environment,
so it never reads — or fails over — an `env_vars` declaration. And
`min run <task>` from inside a session, along with
`min session run <session> <task>`, resolves `inherit` against the daemon's
environment rather than your shell. Both are covered in
[Tasks](https://minimal.dev/docs/reference/tasks#env_vars).

### `[tasks.*]` - Run tasks, scripts, & dev tooling {#tasks}

See: [tasks](https://minimal.dev/docs/reference/tasks).

### `[outputs.*]` - Artifacts produced by `mip materialize` {#outputs}

Each `[outputs.<name>]` section defines an artifact that can be produced with
[`mip materialize <name> -o <path>`](https://minimal.dev/docs/reference/cli-mip#materialize).

```toml
[outputs.<name>]
type = "<output type>"          # optional; defaults to "oci-image"
packages = ["<package>", ...]   # optional; defaults to ["base"] when empty
arch = "<arch>"                 # optional; e.g. "amd64", "arm64"
path = "<path>"                 # raw-file only; required there, invalid for oci-image
entrypoint = "<cmd>"            # oci-image only; string or list of strings
cmd = ["<arg>", ...]            # oci-image only; string or list of strings
vars = { KEY = "value" }        # oci-image only; alias: `env_vars`
```

#### Output types

| `type` | Description |
|--------|-------------|
| `oci-image` | A Linux OCI image archive built from `packages`. Compatible with `docker load` and OCI-compatible registries. |
| `raw-file` | A single file extracted from `packages` at the given `path`. Useful for pulling one artifact (a kernel image, a rootfs image, …) out of a package's file tree. |

#### Fields

- **`type`**: The output kind, either `oci-image` or `raw-file`. Defaults to `oci-image` when omitted.
- **`packages`**: Packages to include in the materialized output. When omitted or empty, defaults to `["base"]`.
- **`arch`**: Target architecture for OCI images. Common values: `amd64`, `arm64`. The CLI flag [`--arch`](https://minimal.dev/docs/reference/cli-mip#materialize) overrides this; if neither is set, the host architecture is used.
- **`path`**: _(`raw-file` only)_ Path, relative to the package file tree, of the single file to extract. Required for `raw-file` outputs; supplying it on an `oci-image` output is an error.
- **`entrypoint`**: _(`oci-image` only)_ OCI image entrypoint. May be a string (`"/app/server"`) or a list (`["/bin/sh", "-c"]`).
- **`cmd`**: _(`oci-image` only)_ OCI image default command. Same string-or-list shape as `entrypoint`.
- **`vars`** (alias: `env_vars`): _(`oci-image` only)_ Environment variables baked into the image as a `KEY = "value"` table.

Setting an `oci-image`-only field (`entrypoint`, `cmd`, `vars`) on a `raw-file` output, or setting `path` on an `oci-image` output, is rejected when the `minimal.toml` is parsed.

#### Examples

A minimal OCI image of just the `base` packages, useful for ad-hoc shells:

```toml
[outputs.base-image]
type = "oci-image"
```

```sh
mip materialize base-image -o ./base.tar
```

A server image with extra packages, an entrypoint, and an env var:

```toml
[outputs.app]
type = "oci-image"
arch = "arm64"
packages = ["base", "openssl"]
entrypoint = "/app/server"
vars = { PORT = "8080" }
```

```sh
mip materialize app -o ./app.tar
```

Override the architecture at the command line:

```sh
mip materialize app --arch amd64 -o ./app-amd64.tar
```

A `raw-file` output extracts a single file from a package. Here the kernel
`Image` is pulled out of the `virtio-kernel-raw` package:

```toml
[outputs.virtio-kernel]
type = "raw-file"
packages = ["virtio-kernel-raw"]
path = "usr/share/virtio-linux/Image"
```

```sh
mip materialize virtio-kernel -o ./Image
```

### `[params]` - Repo-wide parameters {#params}

Declares named parameters available to tasks across the repository, using the
same `{type, help, default}` shape as per-task [`args`](https://minimal.dev/docs/reference/tasks). Every
`[params]` entry must declare a `default`.

Each parameter acts as a defaulted arg on every task: a command reads it as
`%{name}`, and `--name <value>` overrides it for one run. A task that
declares an arg of the same name uses its own arg instead.

```toml
[params]
greeting = { type = "string", default = "hi" }

[tasks.hello]
bash = "echo %{greeting}"  # `hi`, or `bye` with `--greeting bye`
```

### `[cache]` - Artifact cache behavior {#cache}

```toml
[cache]
index_source = "auto"  # optional: "auto" (default), "pinned", or "root"
fetch_retries = 2      # optional: retry count for remote fetches
```

### `[stdlib]` - Standard library requirements {#stdlib}

```toml
[stdlib]
minimum_version = "<version>"  # optional; alias: min_version
```

Refuses to operate when the upstream's standard library is older than the
declared minimum.
