Reference

Manifest Reference

stack.toml lives in your project root and is meant to be committed — it's the whole "share a stack" file, the non-container equivalent of a Dockerfile. Every field below is optional unless marked required; a bare stack.toml with just a [project] section is valid.

[project]

FieldTypeDefaultNotes
namestringfolder nameUsed to key stack status/stack logs/stack down and as the default domain prefix.
domainstring{name}.localhost.localhost resolves with zero setup in Chrome/Firefox/Edge — no hosts-file edit. Non-browser clients (curl, backend-to-backend calls) fall back to 127.0.0.1:<port>.
stack.toml
[project]
name = "acme-api"
domain = "acme-api.localhost"

[[clone]]

Array of tables. Lets a bare stack.toml, dropped in an empty folder, bootstrap the whole project — stack up clones anything missing before doing anything else, and never touches a path that already exists.

FieldTypeDefaultNotes
repostringrequiredAnything git clone accepts.
pathstring"."Where it's cloned to, relative to the project root.
refstringdefault branchBranch, tag, or commit SHA — cloned, then checked out separately (checkout accepts all three; --branch alone doesn't).

[language.<name>]

Either a bare version string, or a table when you need more control — both forms can coexist as siblings, same as Cargo.toml's [dependencies].

stack.toml
[language]
php = "8.3.1"       # simple form
node = "20.11.0"

[language.rust]      # detailed form
version = "1.75.0"
manager = "vfox"
FieldTypeDefaultNotes
versionstringrequired unless path set 
managerstringinferredphp/node → vfox, python → uv. Any other name needs manager set explicitly.
pluginstringthe table keyvfox plugin name override, for the rare mismatch (e.g. Node's plugin is named nodejs).
binarystring{name}.exeBinary filename override.
pathstringBYO — a fixed, already-resolved binary. Bypasses vfox/uv entirely.

Every project pinning the same language+version shares the one binary from vfox's/uv's own store — nothing is copied per project. Activation (see Everyday use) puts it first on PATH the moment you cd in.

[service.<name>]

FieldTypeDefaultNotes
versionstringrequiredAlso the key every other project shares an instance by — same engine+version reuses the one running process.
schemastringproject nameProject isolation is by schema/database name, not a separate data directory per project.
portnumberengine default3306/5432/27017 for mysql/postgres/mongo; required explicitly for any other engine name.
commandstringengine defaultBuilt in for mysql/postgres/mongo; required for anything else. {port}/{data_dir}/{path} placeholders available.
pathstringBYO binary, this project only. Omit to resolve by version via stack register instead.
externalboolfalseAdopt an already-running instance — stack only verifies it's listening, never starts/stops it. Errors if path/command are also set, or if nothing's actually listening.
stack.toml
[service.mysql]
version = "8.0.35"
schema = "acme_api"   # optional — defaults to the project name

[run]

The one process that needs a stable, routed domain — usually your app's own dev server. Omit [run] entirely and stack still activates languages and starts declared services; it just prints that nothing is routed.

FieldTypeDefaultNotes
commandstringrequired unless external{port} is substituted; PORT is also set as an env var, for anything reading process.env.PORT.
portnumberephemeralPin it if anything (OAuth callback, CORS allowlist, a proxy target) hardcodes the port — stack errors clearly rather than silently picking a different one when it's taken.
cwdstringproject rootWhich cloned path the command runs in — matters once [[clone]] has more than one entry.
externalboolfalseRun the dev server yourself, in your own terminal — stack only validates the port and routes to it, never spawns or tracks the process. port becomes required. See below.

Why [run].external exists

Sometimes you'd rather run the dev server yourself — watch its own output directly in your own terminal, keep whatever reload workflow you already use, or just not hand the process over to another supervisor. external = true is built for that: stack's only job becomes routing a real domain to the port you tell it about.

stack.toml
[run]
external = true
port = 8000

[tool.<name>]

FieldTypeDefaultNotes
pathstringBYO — any tool you've already installed. Its directory is injected into the [run] process's PATH.
versionstringFor the small set of tools stack knows how to fetch itself (Composer today), or resolved via stack register for anything you've registered globally.

Command placeholders

Any {VAR_NAME} inside a command string resolves in order: reserved keywords ({port}, {data_dir}, {path}) first, then the process environment, then — only with stack up --prompt — an interactive prompt, masked for anything that looks like a secret. Nothing left unresolved fails silently: every unresolved name is reported together, not one at a time.