Documentation

CLI

Database commands

Generate and apply database migrations, check migration status and push schema changes in development with vitnode db.

The vitnode db commands manage your database schema through migrations: files that describe each change, which you commit so every database gets the same changes in the same order.

vitnode db generate   # write a migration for your schema changes
vitnode db migrate    # apply pending migrations and seed initial data
vitnode db status     # check the connection and what is pending
vitnode db push       # sync the schema directly, development only

Run them in the app that owns the database, the one with drizzle.config.ts (apps/web in a single app with Turborepo, apps/api in a monorepo app), through your package manager: pnpm vitnode db status. They use your app's own Drizzle configuration, connection and migration table.

The scripts in a generated app

A generated app also has two scripts that call older, hidden commands. They are what pnpm dev and the beginner guides use:

ScriptRunsGeneratesAppliesSeedsAsks first
pnpm db:preparevitnode db:prepareYesYesYesNo
pnpm db:migratevitnode migrateYesYesYesNo
-vitnode db migrateNoYesYesYes, unless --yes

vitnode dev runs the same steps as db:prepare before the server starts, so in development you rarely call any of them yourself. In CI and production, use vitnode db migrate --yes. It applies only the migrations you committed and never writes a new one on the server.

Generate, migrate or push?

CommandWhat it changesUse it
db generateWrites a migration fileAfter changing tables, before committing
db migrateApplies migration filesEverywhere: development, CI, production
db pushChanges the database directlyQuick experiments in development

Generate a migration

vitnode db generate --name notifications
ā—† VitNode
  Database

  āœ“ Checking migration history  2.7s
  āœ“ Comparing schema with the last migration  3.1s

Schema changes detected
  + table notifications
  + table notification_preferences
  + column users.notification_count

  āœ“ Generating migration  3.1s

  āœ“ 20261005233216_notifications
  Apply it with vitnode db migrate.

The list comes from drizzle-kit's own plan, so it is exactly what the migration contains. With no changes, the command prints No schema changes - nothing to generate. and exits with 0.

When drizzle-kit cannot tell whether a column was renamed or replaced, the command lists those changes and hands the terminal to drizzle-kit so you can answer. Without an interactive terminal it stops with exit code 2 instead of guessing.

Build plugins first

Migrations are generated from each plugin's build output. After changing a plugin's tables, build the plugin (its build:plugins script, or let pnpm dev rebuild it), then run vitnode db generate.

Apply migrations

vitnode db migrate
ā—† VitNode
  Database migration

  āœ“ Connecting to the database  26ms

Pending migrations
  ā—‹ 20261004081732_add_core_notifications
  ā—‹ 20261004112416_allow_null_notification_digest_schedule

ā—‡ Apply 2 migrations? Yes
  āœ“ Applying migrations  840ms
  āœ“ 20261004081732_add_core_notifications
  āœ“ 20261004112416_allow_null_notification_digest_schedule

  āœ“ Database is up to date.

db migrate applies all pending migrations in one transaction, so a failed migration leaves nothing half-applied, and the error shows Postgres' own details. It then seeds the data every VitNode installation needs, such as languages, roles and permissions.

The seed also runs when no migration is pending. That way a language you add to your config gets its row without a new migration, and db migrate stays safe to run on every deployment.

If another process is already preparing the same database, for example a second container starting at the same time, db migrate waits for it to finish instead of racing it.

Without an interactive terminal, confirm with --yes:

vitnode db migrate --yes

Without --yes, a non-interactive run with pending migrations exits with code 2 instead of waiting for an answer.

Check the status

vitnode db status
ā—† VitNode
  Database

  Provider   PostgreSQL
  Database   vitnode @ localhost:5432
  Status     ā— connected

Migrations
  Applied   64
  Pending   1

Pending
  ā—‹ 20261005233216_notifications

db status only reads. It always connects, so the status is never guessed from the config alone. It warns about an applied migration whose file changed since it ran, or whose file is missing. When the database does not answer within 10 seconds, it exits with code 1.

Push the schema in development

vitnode db push
ā—† VitNode
  Push database schema

  ! Direct schema synchronization is intended for development.

Changes
  - table zz_drafts
  + index notifications.notifications_user_id_idx

Data loss
  ! table zz_drafts (non empty)

ā—‡ Apply 2 changes, including 1 that deletes data? No

db push changes the database without writing a migration. It shows every change first and names each one that deletes data. The data-loss question defaults to No.

From a script, pass --yes to apply and --accept-data-loss to allow changes that delete data. Without --accept-data-loss, a non-interactive run that would delete data exits with code 2.

db push refuses production

When NODE_ENV is production, vitnode db push stops before connecting and exits with code 2. Use db generate and db migrate instead. Pass --force only if you really mean to push to that database.

Options

CommandOptionEffect
db generate--name <name>Name the migration folder
db migrate-y, --yesApply without asking
db push-y, --yesApply without asking
db push--accept-data-lossAllow changes that delete data
db push--forceAllow pushing when NODE_ENV is production

Every command also accepts --plain and --verbose. With --verbose, the change lists are not shortened and a failure shows the complete drizzle-kit output.