Skip to content

Environment โ€‹

Each app keeps its own environment variables. mono-env is a small wrapper (shipped with mono-utils) that loads a root env file first, then layers each app's env file from .apps/ on top, and finally runs your real command (vite, vue-tsc, โ€ฆ) with the merged environment.

You don't call it directly โ€” it's already wired into every script in the templates, so day to day you just run pnpm dev / pnpm build.

mono env -e .env.dev -- vite

That single line means: load .env.dev (root, then every app), then run vite.

File layout โ€‹

The .apps/ folder is produced by Sync. Each app brings its own env file, alongside the root env files:

mono-vue-remote/
โ”œโ”€โ”€ .apps/
โ”‚   โ””โ”€โ”€ mono-host/
โ”‚       โ””โ”€โ”€ .env.dev          # app env (overrides root)
โ”œโ”€โ”€ .env.dev                  # root env (loaded first)
โ”œโ”€โ”€ .env
โ””โ”€โ”€ package.json

How it loads โ€‹

mono-env loads env files in order, and later files override earlier ones:

  1. Root โ€” the file you pass with -e, resolved from the app you're running (e.g. .env.dev).
  2. Per app โ€” for every folder inside .apps/, the file with the same name inside it (e.g. .apps/mono-host/.env.dev).

Matched by file name

Per-app files are matched by the basename of your -e value. -e .env.dev loads .apps/<app>/.env.dev; -e .env loads .apps/<app>/.env. Missing files are skipped with a warning โ€” they don't stop the command.

Naming convention โ€‹

mono-env loads every key into the environment of the command it runs โ€” but not every key reaches your client code. Vite only exposes variables matching the prefixes set in vite.config.ts:

ts
// vite.config.ts
envPrefix: ['VITE_', 'MONO_'],

That gives two prefixes with different roles, plus everything else:

PrefixRead withUse for
MONO_import.meta.env.MONO_*The only shared vars. Cross-app / mono-repo config exposed to the client bundle.
VITE_import.meta.env.VITE_*App-local client config (standard Vite).
(no prefix)not exposedBuild/tooling-only secrets (tokens, generators). Stays in process.env for the running command, never shipped to the browser.

Standard: shared = MONO_

Treat MONO_ as the contract between apps. Anything an app needs to read from another app โ€” or anything the Host shares with Remotes โ€” gets a MONO_ prefix so it's exposed via import.meta.env. Keep app-private values on VITE_, and keep secrets prefix-less so they never end up in the client bundle.

ts
// Shared, exposed to the client because of the MONO_ prefix
const port = import.meta.env.MONO_HOST_PORT

package.json scripts โ€‹

The templates already route every command through mono-env. The relevant lines from mono-vue / mono-host:

json
{
  "scripts": {
    "dev": "mono env -e .env.dev -- vite",
    "dev:h": "mono env -e .env.dev -- vite --host",
    "build": "mono env -e .env -- vue-tsc && mono env -e .env -- vite build --emptyOutDir",
    "preview": "mono env -e .env -- vite preview"
  }
}

Everything after -- is the command to run. Dev scripts use .env.dev; build and preview use .env.

Flags โ€‹

FlagAliasDescription
-e <file>--envEnv file to load. Repeatable โ€” pass -e multiple times to load several.
-a <name>--appLoad only .apps/<name> instead of every app folder.
-r <dir>--app-rootFolder to scan for apps. Defaults to .apps.
--Everything after this is the command to run.

Example โ€” load env for a single app only:

mono env -e .env.dev -a mono-host -- vite

Injected variables โ€‹

mono-env also exposes which app(s) it loaded, so your command can read them:

VariableWhenValue
MONO_APP_NAMEone app loadedthe app folder name (e.g. mono-host)
MONO_APP_DIRone app loadedabsolute path to that app folder
MONO_APP_NAMESmultiple apps loadedcomma-joined list of app names

Multiple apps โ€‹

When no -a is given and .apps/ has more than one folder, mono-env loads them all. Because each app's env layers on top of the previous one, keys with the same name collide โ€” the last app wins.

Use unique keys per app

If two apps both define MONO_API_URL, whichever loads last overrides the other. Since shared vars use the MONO_ prefix, keep them unique per app โ€” e.g. MONO_HOST_API_URL, MONO_VUE_API_URL โ€” or scope each run to one app with -a.

See Sync for how the .apps/ folder gets populated.