Skip to content

Configuration

migronaut resolves configuration from four sources, in priority order:

CLI flags  >  Environment variables  >  Config file  >  Defaults

A config file is never required — env vars alone are always sufficient.

Config file

On startup, migronaut looks in the current working directory for the first of:

  1. migronaut.config.ts
  2. migronaut.config.js
  3. migronaut.config.json

Generate one with migronaut init. Override discovery with --config <path>.

js
export default {
  uri: process.env.MIGRONAUT_URI ?? 'mongodb://localhost:27017',
  dbName: 'my_app',
  migrationsDir: './migrations',
  migrationsCollection: '_migronaut_migrations',
  strict: false,
  useTransaction: false,
  createExtension: 'js',
};
ts
import type { MigronautConfig } from '@alexify/migronaut';

const config: Partial<MigronautConfig> = {
  uri: process.env.MIGRONAUT_URI ?? 'mongodb://localhost:27017',
  dbName: 'my_app',
  migrationsDir: './migrations',
  createExtension: 'ts',
};

export default config;
json
{
  "uri": "mongodb://localhost:27017",
  "dbName": "my_app",
  "migrationsDir": "./migrations"
}

Async / factory config (secret managers)

A .ts/.js config may export default a function (sync or async) that returns the config. This is the dependency-free way to load a connection from a secret manager at runtime — the library ships no cloud SDKs, you bring your own inside the function:

ts
import type { MigronautConfigInput } from '@alexify/migronaut';

const loadConfig: MigronautConfigInput = async () => {
  const { uri, dbName } = await fetchFromSecretsManager(); // your code
  return { uri, dbName, migrationsDir: './migrations' };
};

export default loadConfig;

Generate a ready-made AWS Secrets Manager template with migronaut init --secret-provider (swap the body for Google/Vault/Azure/any source — it just must return { uri, dbName }).

All options

OptionTypeDefaultDescription
uristringMongoDB connection URI (required)
dbNamestringDatabase name (required)
migrationsDirstring'./migrations'Directory holding migration files
migrationsCollectionstring'_migronaut_migrations'Collection storing the changelog
lockCollectionstring'_migronaut_locks'Collection used for the concurrency lock
lockTTLSecondsnumber60Seconds before a lock is considered stale
strictbooleanfalseAbort (vs. warn) on a checksum mismatch
useTransactionbooleanfalseWrap every migration in a transaction globally
fileExtensionsstring[]['.ts', '.js']Extensions scanned in the migrations dir
createExtension'ts' | 'js''js'Default file type for migronaut create
sequentialbooleanfalseUse 0001- numbering instead of timestamps
templatePathstringPath to a custom migration template
environmentstringNODE_ENV'production'Value stamped on the environment field of changelog records
onLockLost'abort' | 'warn''abort'What to do if the lock is lost mid-run
envFilestring | false'.env'.env file to load, or false to load none
ensureIndexesbooleantrueCreate the changelog indexes on first connect
clientOptionsMongoClientOptionsDriver options: TLS, AWS IAM / X.509 auth, proxies, pool sizing
clientMongoClientAn already-connected client to reuse; migronaut never closes it
timeoutMsnumberStop the run when one migration exceeds this (best-effort)
reloadMigrationsbooleanfalseBypass the ESM module cache (long-lived processes only)
mongooseMongooseMongoose instance, if your migrations use it
hooksMigrationHooksLifecycle hooks
loggerMigronautLogger | nullbuilt-inCustom logger (pino-compatible {debug, info, warn, error} — a pino instance works directly); null silences all output

Log methods receive an optional second argument with structured fields — { runId, migration, direction, batch, durationMs } — so a machine-readable logger does not have to parse the human string. A pino-style logger (one with child()) gets them in pino's own (fields, msg) order instead. A plain one-argument logger keeps working unchanged.

runId is a per-run correlation id: it is also the lock's owner token and is stored on every changelog record that run writes, so a leftover lock can be traced to the exact migrations it was holding.

Connection timeout

The driver waits up to 30 s (its default serverSelectionTimeoutMS) before a connection failure surfaces — a long time for a boot-time runMigrations or a serverless cold start. Tighten it via clientOptions:

js
clientOptions: { serverSelectionTimeoutMS: 5000 }

Environment variables

Every scalar option has an MIGRONAUT_* variable — which is what makes "a config file is never required" literally true, not just a slogan. These override the config file:

Env varMaps to
MIGRONAUT_URIuri
MIGRONAUT_DBdbName
MIGRONAUT_MIGRATIONS_DIRmigrationsDir
MIGRONAUT_COLLECTIONmigrationsCollection
MIGRONAUT_LOCK_COLLECTIONlockCollection
MIGRONAUT_LOCK_TTLlockTTLSeconds
MIGRONAUT_STRICTstrict
MIGRONAUT_USE_TRANSACTIONuseTransaction
MIGRONAUT_SEQUENTIALsequential
MIGRONAUT_CREATE_EXTENSIONcreateExtension
MIGRONAUT_ENVIRONMENTenvironment
MIGRONAUT_TEMPLATE_PATHtemplatePath
MIGRONAUT_TIMEOUT_MStimeoutMs
MIGRONAUT_ON_LOCK_LOSTonLockLost
MIGRONAUT_ENSURE_INDEXESensureIndexes
MIGRONAUT_RELOAD_MIGRATIONSreloadMigrations
MIGRONAUT_ENV_FILEenvFile

The remaining options — fileExtensions, clientOptions, and the live instances client, mongoose, hooks, logger — are config-file/API only. They hold arrays, objects or live handles, which a single environment string cannot express.

Values are rejected, never coerced

A value that doesn't parse fails the run with a CONFIG_INVALID error naming the variable. MIGRONAUT_STRICT=on does not quietly mean false, and MIGRONAUT_LOCK_TTL=abc does not quietly mean "no TTL" — a typo in a safety setting must never be the reason a safety setting is off.

Booleans accept true/1/yes and false/0/no, case-insensitive and trimmed. Numbers must be positive integers.

Variables that shape the CLI, not the config

Env varEffect
MIGRONAUT_NO_COLORDisable ANSI color for migronaut only
MIGRONAUT_FORCE_COLORForce color on even when piped; 0 forces it off
MIGRONAUT_USERWho to record in a changelog record's executedBy, overriding the OS user

Color precedence, most specific first: MIGRONAUT_FORCE_COLOR > MIGRONAUT_NO_COLOR > FORCE_COLOR > NO_COLOR > TERM=dumb > whether the stream is a TTY. The --no-color flag beats all of them. The prefixed pair is there so you can pin migronaut's own output without disturbing every other tool in the same shell; the unprefixed pair is still honored underneath, because no-color.org is an ecosystem-wide convention. There is no MIGRONAUT_TERMTERM describes what your terminal can render, not what migronaut should do.

MIGRONAUT_USER matters in CI, where the OS user is a meaningless runner or root and the identity worth stamping on the changelog is the deploy actor.

.env files

.env is loaded from the working directory before env vars are read. Point elsewhere with --env-file <path> (or MIGRONAUT_ENV_FILE), or skip it entirely with --no-env / envFile: false — worth doing in CI, so a stray .env cannot silently outrank a committed config. Loading uses Node's native util.parseEnv — always present on the supported Node range (≥ 22.18), no dependency needed. Real environment variables always win over .env values. Files above 1 MB (or anything that is not a regular file) are rejected with CONFIG_INVALID. Supported syntax: one KEY=VALUE per line, optional export prefix, matching quotes, full-line and inline # comments, and multiline values inside double quotes; ${VAR} interpolation is not supported.

bash
# .env
MIGRONAUT_URI=mongodb://localhost:27017
MIGRONAUT_DB=my_app

Global CLI flags

These flags work on every command and have the highest precedence:

FlagOverrides
--uri <uri>MIGRONAUT_URI / uri
--db <name>MIGRONAUT_DB / dbName
--dir <path>MIGRONAUT_MIGRATIONS_DIR / migrationsDir
--config <path>Config file auto-discovery
bash
migronaut up --uri "mongodb://localhost:27017" --db my_app --dir ./db/migrations

Released under the MIT License.