Env Validation
Miso automatically loads
.envfiles into your scripts — you don’t need--envfor that. The--envflag andmiso envare for validation: checking that variables exist and match their expected types before running.
Usage
miso env validates environment variables from your .env files against rules defined in miso.json.
miso envOr use --env as a flag behind other commands to validate before running:
miso dev --env
miso build --envQuick Start
- Add an
envarray tomiso.json, with one entry per app or.envfile. Each root entry needs ascope— a target name or"global":
{
"env": [
{
"scope": "web",
"label": "web",
"path": "apps/web/.env.local",
"variables": {
"PORT": "port",
"DATABASE_URL": "url"
}
},
{
"scope": "api",
"label": "api",
"path": "apps/api/.env",
"variables": {
"REDIS_URL": {
"type": "url",
"schemes": ["redis", "rediss"]
}
}
}
]
}- Run
miso envbefore starting your app (or in CI):
miso env && miso devEach entry is validated independently, and every failure is reported in one pass — grouped under its label, one variable per line:
api
REDIS_URL: missing required variableWhat gets checked
miso env and --env read the root miso.json and every workspace member’s miso.json, in every repo mode. A clean exit means the whole repo’s declared variables are present and well-typed — that’s what makes it usable as a CI gate.
Two consequences worth knowing:
-
A member
miso.jsonthat won’t parse fails the run. Skipping it would report a pass over requirements nothing checked, which is the failure mode the gate exists to prevent.failed to read a workspace config error="load apps/web/miso.json: parse config: …" -
Membership is the package manager’s. Members come from
package.jsonworkspaces orpnpm-workspace.yaml. Amiso.jsonin a directory that isn’t a workspace member isn’t traversed — if you keep schemas outside the workspace set, declare them at the root instead.
Auto-Discovery
Even without an env block in miso.json, Miso automatically loads a .env file into every script it runs. It walks the following candidates in order and loads the first one it finds:
| Priority | File |
|---|---|
| 1 | .env.local |
| 2 | .env.production |
| 3 | .env.development |
| 4 | .env |
Variables already set in the shell environment take precedence over values in the file — Miso never overrides a variable that is already present in the process environment.
In a monorepo, auto-discovery searches that member’s own directory rather than the project root when you run a workspace-scoped script.
Scope & Resolution
Once you add an env block, each root entry needs a scope — a target name or "global" — so miso knows which command it applies to. Resolving the env for a run target layers global entries, then either the entries scoped to that target or (for a member declaring its own env) the member’s <member>/miso.json entries, later winning on key conflict. The result still yields to anything already set in your shell. A given scope lives in the root config or the member’s, never both — declaring it twice is an error.
miso env enforces scope strictly and fails on a missing one; loading env into a running script is permissive and just skips entries that don’t match. In a delegated repo (repo: "turbo"/"nx") the delegate resolves targets, so root entries don’t need a scope there — but validation still traverses every workspace member’s schema, and miso injects env for any task a repo.tasks override hands it to run. See Configure for the full breakdown.
Next Steps
- Configure labels, paths, required variables, and validation rules
- Env generation — from keys-only templates to populated, validated production envs
- See the Env Var Types reference for all supported types and options