Runtime Environments and Flavors
This guide explains how runtime environments and flavors work in Vona within the Cabloy monorepo.
Why this model exists
Vona does not treat backend runtime selection as a single switch.
In practice, backend behavior is chosen by combining:
- runtime environment
- flavor
That gives Cabloy enough room for development, testing, Playground, Docker, CI, and deployment-specific behavior without flattening everything into one axis.
Runtime environments
Vona provides three main runtime environments:
| Name | Description |
|---|---|
test | testing environment |
dev | development environment |
prod | production environment |
How runtime environment is activated in the current repo
In Cabloy Basic, the active mode is usually implied by which command you run.
Root-level contributor workflow
npm run dev
npm run test
npm run build
npm run startThese root scripts delegate to the backend or frontend layer as needed, so they are the preferred monorepo-facing entrypoints.
Direct Vona workflow
Representative backend-side scripts include:
cd vona && npm run dev
cd vona && npm run dev:one
cd vona && npm run test
cd vona && npm run cov
cd vona && npm run db:reset
cd vona && npm run build
cd vona && npm run build:docker
cd vona && npm run start
cd vona && npm run start:one
cd vona && npm run start:dockerIn the current repo, those scripts map naturally to:
devcommands -> development modetest/cov/db:reset-> test modebuild/start-> production mode
Flavors
For more specific scenarios, Vona adds the flavor dimension.
The combination of runtime environment and flavor lets the framework support more precise operational contexts without inventing a separate environment for every case.
Built-in flavors used in this repo
| Name | Description |
|---|---|
normal | default contributor/runtime flavor |
play | used by the Playground workflow |
docker | used for Docker-oriented build/runtime output |
ci | used for CI-oriented production variants |
Representative current scripts include:
cd vona && npm run play
cd vona && npm run build
cd vona && npm run build:docker
cd vona && npm run start
cd vona && npm run start:dockerThese scripts show that the same prod mode can still branch into different output/runtime shapes through flavor.
What the CLI injects automatically
The current Vona CLI does not only read env files. It also injects key meta variables for the running process.
The important ones are:
NODE_ENVMETA_MODEMETA_FLAVORSERVER_WORKERS
A practical interpretation is:
META_MODEis the backend runtime mode source of truthMETA_FLAVORcarries the flavor dimensionNODE_ENVis derived from mode (test,development, orproduction)SERVER_WORKERSis normalized so the runtime always has an explicit worker count
In the current CLI behavior:
- prod defaults
SERVER_WORKERSto CPU count when not provided - non-prod defaults
SERVER_WORKERSto1
Curated built-in env variable catalog
The current repo uses many env variables, but these are the most important ones for understanding the runtime/config family.
| Variable family | Representative variables | Why it matters |
|---|---|---|
| Runtime meta | META_MODE, META_FLAVOR, NODE_ENV | chooses the active runtime and flavor shape |
| Worker/runtime process | SERVER_WORKERS | controls worker count and is normalized by the CLI/bootstrap path |
| HTTP server | SERVER_LISTEN_HOSTNAME, SERVER_LISTEN_PORT, SERVER_LISTEN_DISABLE, SERVER_SERVE_PROTOCOL, SERVER_SERVE_HOST, SERVER_GLOBALPREFIX | controls listen/serve behavior and URL shaping |
| Database | DATABASE_DEFAULT_CLIENT, DATABASE_CLIENT_SQLITE3_FILENAME, DATABASE_CLIENT_PG_*, DATABASE_CLIENT_MYSQL_* | controls datasource defaults and concrete client connection settings |
| Redis | REDIS_DEFAULT_HOST, REDIS_DEFAULT_PORT, REDIS_DEFAULT_DB | controls the backend Redis baseline used by queue, cache, broadcast, and related capabilities |
| Logger | LOGGER_DIR, LOGGER_ROTATE_* | controls log path and rotation behavior |
Use this page as the runtime-facing overview, then inspect the current app config when you need to see how those values are translated into backend config.
How to determine runtime metadata in code
Via environment variables
process.env.META_MODE === 'test';
process.env.META_MODE === 'dev';
process.env.META_MODE === 'prod';
process.env.META_FLAVOR === 'normal';
process.env.META_FLAVOR === 'docker';
process.env.META_FLAVOR === 'ci';Using environment variables is especially useful when behavior needs to participate in build-time replacement or tree-shaking.
Via config
app.config.meta.mode === 'test';
app.config.meta.mode === 'dev';
app.config.meta.mode === 'prod';
app.config.meta.flavor === 'normal';
app.config.meta.flavor === 'docker';
app.config.meta.flavor === 'ci';Via simplified helpers
app.meta.isTest;
app.meta.isDev;
app.meta.isProd;A practical rule is:
- use
process.envwhen the logic is environment-driven and may affect bundling or compile-time replacement - use
app.config.metaorapp.metawhen the logic belongs to normal runtime behavior inside the running backend
Env-file resolution and precedence
This page owns the env-file and mode/flavor precedence view. For config surfaces and config layering, see Config Guide. For the fuller instance-aware merge view used by request-scoped behavior, see Multi-Instance and Instance Resolution.
In the current repo, env values are loaded from the vona/env/ directory.
Representative files include:
.env.env.dev.env.dev.play.env.test.env.prod.env.prod.ci.env.prod.docker.env.local.env.prod.local.env.prod.docker.local.env.prod.normal.local
The current loader builds the env-file chain from runtime metadata shaped like:
modeflavorlocal
That means the loader can cascade from general files to more specific files such as:
.env.env.prod.env.prod.docker.env.prod.docker.local
The important rule is:
.localvariants are treated as highest-priority local overrides
Representative effective chains in the current repo look like this:
dev + normal->.env->.env.dev->.env.localdev + play->.env->.env.dev->.env.dev.play->.env.localprod + docker + local->.env->.env.prod->.env.prod.docker->.env.local->.env.prod.local->.env.prod.docker.local
One more practical detail matters during bootstrap:
- after the env files are prepared, already-present
process.envvalues still win when the runtime assembles the final env object
So when you document or debug backend configuration, do not assume one flat .env file. Inspect the active mode, flavor, local override files, and any externally injected environment variables together.
Custom flavors
Flavors are not limited to the built-in ones. You can introduce your own flavor for needs such as:
- customer-specific behavior
- deployment-specific behavior
- project-specific behavior
- organization-specific behavior
Example:
npm run dev -- --flavor=customAAnd in code:
process.env.META_FLAVOR === 'customA';
app.config.meta.flavor === 'customA';Type support for custom flavors
Custom flavor names can also be added to type definitions for better editor support.
Representative pattern:
declare module '@cabloy/module-info' {
export interface VonaMetaFlavorExtend {
customA: never;
}
}How runtime metadata becomes backend config
Runtime env values do not stop at process.env.
In the current Vona app config, they are folded into config such as:
config.meta.modeconfig.meta.flavorconfig.server.*config.database.defaultClientconfig.logger.*config.redis.*
This is why runtime environment and config should be read together, not as separate concerns.
For the config-layering view, also see Config Guide.
Relationship to startup, instance, and datasource behavior
Runtime environment and flavor are not only configuration concerns. They also shape:
- startup enable/disable rules
- startup hook behavior in different modes
- distributed capability activation
- datasource defaults
- local-vs-prod operational differences
Read this guide together with:
- Config Guide
- Backend Startup Guide
- Model Guide
- Multi-Database and Datasource Guide
- Queue Guide
- Schedule Guide
- Broadcast Guide
Implementation checks for runtime and flavor changes
When evaluating backend runtime or configuration guidance, do not assume that dev/test/prod is the whole story.
Also inspect:
- which repo command actually drives the workflow
- whether the active behavior depends on flavor
- whether
.localoverrides may be changing the effective env values - whether the runtime check should use
process.env,app.config.meta, orapp.meta - whether a datasource, startup, or distributed feature is mode- or flavor-sensitive
These environment differences also matter for operational concerns such as log directories, log levels, worker count, and runtime output layout; see Logger Guide.