Configuration Guide
This page covers every option available in .vibestackr.yaml (or .vibestackr.json, .vibestack.yml, .vibestackr with no extension) in depth, with worked examples. For a quick overview, see the config section on the main page.
Full example
A stack with a database, an API that depends on it, and a web frontend:
name: MyApp
services:
- name: postgres
cwd: .
command: sh
args: ["-c", "docker start myapp-postgres 2>/dev/null || docker run -d --name myapp-postgres -e POSTGRES_PASSWORD=password -p 5432:5432 postgres:16"]
oneShot: true
liveness: { type: command, command: docker exec myapp-postgres pg_isready -U postgres }
stopCommand: docker stop myapp-postgres
- name: api
cwd: api
command: npm
args: ["run", "dev"]
type: node
note: http://localhost:4000
dependsOn: ["postgres"]
envFile: api/.env
liveness: { type: http, url: http://localhost:4000/health }
onReady: echo "api is up"
- name: web
cwd: web
command: npm
args: ["run", "dev"]
type: node
note: http://localhost:3000
dependsOn: ["api"]
watcher: true
liveness: { type: port, host: localhost, port: 3000 }
dependencies:
- message: "Docker must be running"
when:
- commandFails: { command: docker, args: ["info"] }
warnings:
- message: "DATABASE_URL not set — api will fall back to localhost"
when:
- envUnset: DATABASE_URL
service: api
shortcuts:
- key: m
label: Run migrations
command: npm run migrate
cwd: api
- key: s
label: Seed database with a named fixture
command: npm run seed -- ${fixture}
cwd: api
inputs:
- name: fixture
label: Fixture name
default: default
Top-level fields
name
Brand shown in the TUI corner and terminal title.
Default: run-local
name: MyApp
services[]
Core: one object per managed process. All services are included by default — use --exclude / --only at runtime to scope a run.
vibestackr --only api,postgres
vibestackr --exclude web
dependencies[]
Pre-startup checks that can abort the entire run. Each entry needs a message and an optional when[] (all conditions must be true — see Condition types).
dependencies:
- message: "Node 20+ required"
when:
- commandFails: { command: node, args: ["-e", "process.exit(process.version.split('.')[0].slice(1)>=20?0:1)"] }
warnings[]
Same shape as dependencies[] but non-fatal — printed at startup when conditions match. Can include a service field to scope the warning to one service (skipped entirely if that service is excluded from the run).
warnings:
- message: "Using default Stripe test key"
when:
- envUnset: STRIPE_SECRET_KEY
service: api
shortcuts[]
Single-key TUI actions. Each has key, label, and either restart (restarts a named service) or command (runs a shell command). Supports typed inputs[] for interactive prompts.
Service fields (services[].<field>)
name (required)
Unique identifier. Becomes the tab name, and is used in dependsOn, --exclude/--only, and shortcut references.
type
Free-form label. When set to a framework name (node, go, rust, python), vibestackr runs an automatic first-run install under the service's cwd — npm install, go mod download, cargo fetch, or uv sync/poetry install — but only when the dependency folder (node_modules, etc.) is missing or the manifest has changed since the last run.
- name: api
type: node
cwd: api
command: npm
args: ["run", "dev"]
cwd
Working directory relative to the project root where the command runs.
command (required)
Executable to spawn, e.g. npm, sh, ./gradlew.
args[]
Arguments passed to the command. Either a JSON array (needed when an argument itself contains spaces or shell metacharacters):
args: ["-c", "echo hello && echo world"]
or a space-separated string for simple cases:
args: run dev
env
Object of key-value pairs merged into the service's environment, overriding both process.env and envFile on conflict.
env:
NODE_ENV: development
PORT: "4000"
envFile
Path to a .env file (relative to the project root) loaded into the service's env. Comments (#) and blank lines are skipped. A missing file is silently ignored. env wins over envFile on key conflicts.
envFile: api/.env.local
note
One-line description pinned atop the service's log tab — typically a URL or status string.
note: http://localhost:4000
jsonLog
Configures JSON-line formatting for services that emit structured logs (e.g. pino, winston, Spring Boot). Supports a format template with ${field} and dot-path access, an optional levelField (colors the line: error=red, warn=yellow, debug=grey), and optional colors overrides. Raw lines are always preserved in the log buffer and on disk if --persist-logs is set — formatting only affects the TUI display.
jsonLog:
format: "${time} ${level} ${msg}"
levelField: level
colors:
info: cyan
oneShot
Set true for jobs that exit (builds, migrations, one-off container bootstrap). vibestackr treats these differently: it won't poll liveness unless one is explicitly set, and status flips to "ready" on success only if no liveness is configured.
- name: migrate
cwd: api
command: npm
args: ["run", "migrate"]
oneShot: true
watcher
Informational flag for services that handle their own file watching (nodemon, vite HMR, etc.). Tells MCP clients this service reloads without needing a restart.
autoRestart
If a non-oneShot process exits unexpectedly, respawn it automatically with exponential backoff (1s → 2s → 4s → … → 30s cap). The backoff counter resets once a respawned instance stays up for 10 seconds. Not applied during daemon shutdown.
autoRestart: true
dependsOn[]
Other service names that must be ready before this one starts. Dependencies are evaluated declaratively — no service begins until everything it depends on has passed its liveness gate.
dependsOn: ["postgres", "redis"]
liveness
How vibestackr knows the service is actually ready — see Liveness check types below. One of type: port, type: http, or type: command; each carries an optional timeout in seconds (default 300).
onSuccess
Shell command that fires once if this oneShot service exits with code 0. Fires on process exit regardless of any liveness setting.
onSuccess: echo "migration complete"
onReady
Shell command that fires once this service passes its liveness check (or immediately for oneShot services without liveness). Skipped entirely if liveness times out or fails.
onReady: curl -s http://localhost:4000/warmup
stopCommand
Shell command run in cwd when the service is stopped (restart, TUI 'reload', or shutdown). Use for cleanup of anything that outlives the spawned process, like a docker container.
stopCommand: docker stop myapp-postgres
Liveness check types
{ type: "port", host, port, timeout? }
Succeeds when a TCP connection to host:port succeeds. Default timeout: 300s.
liveness: { type: port, host: localhost, port: 5432, timeout: 60 }
{ type: "http", url, timeout? }
Succeeds when an HTTP GET to url returns a 2xx or 3xx response. Default timeout: 300s.
liveness: { type: http, url: http://localhost:4000/health }
{ type: "command", command, timeout? }
Succeeds when the shell command exits with code 0. Default timeout: 300s. Prefer oneShot: true on services that don't need a real readiness gate — their status will just reflect the process exit code.
liveness: { type: command, command: docker exec myapp-postgres pg_isready -U postgres, timeout: 30 }
Condition types (for dependencies[] and warnings[])
All conditions use a declarative shape. When listed under when[], they're ANDed together — every condition must hold for the dependency/warning to fire.
{ envSet: "VAR" }
Fires when the environment variable exists (any value, including empty string).
when:
- envSet: DATABASE_URL
{ envUnset: "VAR" }
Fires when the environment variable does not exist.
{ envNotIn: { var: "X", values: ["a", "b"] } }
Fires when the variable's current value is not one of the listed values.
when:
- envNotIn: { var: NODE_ENV, values: ["development", "test"] }
{ commandExists: "node" }
Fires when the named executable is found on PATH.
{ commandMissing: "npm" }
The opposite of commandExists — fires when the executable is NOT on PATH.
{ commandFails: { command: "...", args: [...] } }
Fires when the given command exits with a non-zero code.
when:
- commandFails: { command: docker, args: ["info"] }
{ included: "serviceName" }, { excluded: "serviceName" }, { anyIncluded: ["a", "b"] }
Fire based on whether a service is currently included/excluded from the run (e.g. via --exclude/--only). Useful for context-aware warnings — e.g. warn only when a dependency service has been excluded.
warnings:
- message: "api excluded — web will fail to load data"
when:
- excluded: api
service: web
Shortcut fields (shortcuts[].<field>)
key (required)
The single character that triggers the shortcut in the TUI.
label (required)
Appears in the bottom status bar and --help.
restart
A service name. Restarts (or starts, if not running) that service when pressed. Mutually exclusive with command.
- key: r
label: Restart api
restart: api
command
Shell command to run (supports ${name} substitution from inputs[]). Mutually exclusive with restart. Can be paired with cwd for a working directory.
inputs[]
Makes the shortcut interactive. Each entry needs a name, and optional label, placeholder, and/or default. The TUI shows a text box per input; the MCP run_shortcut tool gains typed parameters. Collected values substitute into command by their name.
- key: s
label: Seed database with a named fixture
command: npm run seed -- ${fixture}
cwd: api
inputs:
- name: fixture
label: Fixture name
placeholder: e.g. large-dataset
default: default
Config format options
.vibestackr.yamlor.vibestack.yml— parsed as YAML.vibestackr.jsonor.vibestackr(no extension) — parsed as JSON--config path— load any known format by explicit path
The schema lives at schema/config.schema.json; it's also what editor autocomplete and vibestackr init use as the source of truth.