Manual contentsCLI ReferenceBrowse 103 chapters
Manual 6 min read

CLI Migrations

The suprnova developer CLI shells into your application binary to drive SeaORM's migration runner, so the same migration set executes whether you run it from a developer terminal, from CI, or implicitly at server startup. Use these commands to author migration files, apply them, roll back, and keep your generated SeaORM entities in sync with the schema.

For the schema-authoring API (column types, indexes, foreign keys, the full MigrationTrait), see Migrations. For inserting test data after the schema lands, see Seeding.

make:migration

Generate a new migration file under src/migrations/ and wire it into the Migrator in src/migrations/mod.rs.

suprnova make:migration <name>

<name> is normalised to snake_case. The generator recognises the standard naming patterns and uses them to pick the DeriveIden enum:

  • create_<table>_table - scaffolds a create_table body
  • add_<column>_to_<table> - scaffolds a stub for alter_table
  • drop_<table>_table - scaffolds a drop_table body
  • anything else - uses the name as the table identifier

Examples

suprnova make:migration create_users_table
suprnova make:migration add_email_to_users
suprnova make:migration drop_legacy_sessions_table

Generated file

The file is written to src/migrations/m{YYYYMMDD}_{HHMMSS}_<name>.rs (for example m20260530_142301_create_users_table.rs) and added to the Migrator::migrations() vec.

use sea_orm_migration::prelude::*;

#[derive(DeriveMigrationName)]
pub struct Migration;

#[async_trait::async_trait]
impl MigrationTrait for Migration {
    async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
        manager
            .create_table(
                Table::create()
                    .table(Users::Table)
                    .if_not_exists()
                    .col(
                        ColumnDef::new(Users::Id)
                            .integer()
                            .not_null()
                            .auto_increment()
                            .primary_key(),
                    )
                    .col(
                        ColumnDef::new(Users::CreatedAt)
                            .timestamp()
                            .not_null()
                            .default(Expr::current_timestamp()),
                    )
                    .col(
                        ColumnDef::new(Users::UpdatedAt)
                            .timestamp()
                            .not_null()
                            .default(Expr::current_timestamp()),
                    )
                    .to_owned(),
            )
            .await
    }

    async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
        manager
            .drop_table(Table::drop().table(Users::Table).to_owned())
            .await
    }
}

#[derive(DeriveIden)]
enum Users {
    Table,
    Id,
    CreatedAt,
    UpdatedAt,
}

Edit the generated file to declare your columns, indexes, and constraints. See Migrations for the full schema-builder surface.

migrate

Run every pending migration in src/migrations/.

suprnova migrate

The CLI shells out to cargo run -- migrate so your app's Application runner does the work - same binary, same Migrator, same database connection that serve would use.

Running migrations...
Migrations completed successfully!

The serve / web:run path auto-runs migrate before binding the socket unless you opt out with --no-migrate or set SUPRNOVA_AUTO_MIGRATE_BEST_EFFORT=true to keep going past a failure. A migration error during auto-migrate exits non-zero before the server boots; see framework/src/app/mod.rs for the fail-closed contract.

migrate:status

Print the applied/pending state of every migration.

suprnova migrate:status
Migration status:
...SeaORM-formatted table of applied/pending migrations...

The body of the report comes from SeaORM's MigratorTrait::status, so the exact formatting tracks the SeaORM version your app depends on.

migrate:rollback

Roll back the last applied migration (or the last N).

suprnova migrate:rollback [--step <N>]
Option Default Description
--step <N> 1 Number of migrations to roll back
# Roll back one migration
suprnova migrate:rollback

# Roll back the last three
suprnova migrate:rollback --step 3
Rolling back 3 migration(s)...
Rollback completed successfully!

Each migration's down() runs in reverse application order. A failing down() exits non-zero and leaves the rest of the chain untouched - nothing further is attempted.

migrate:fresh

Drop every table in the database and re-run every migration from scratch.

suprnova migrate:fresh
WARNING: Dropping all tables and re-running migrations...
Database refreshed successfully!

This destroys all data in the connected database. It is meant for local development and test setup, not for any environment where the data matters.

The production guard

Outside production it runs immediately, with no prompt - dropping a local database is routine and a confirmation you always answer the same way trains you to stop reading it.

When APP_ENV resolves to production it demands two different kinds of proof:

suprnova migrate:fresh --force   # …then type the environment name when asked
  1. --force proves intent at the moment you typed the command.
  2. A typed confirmation on an interactive terminal proves a human is present.

The terminal requirement is the point of the second one. Without it, echo production | suprnova migrate:fresh --force in a deploy script would answer the prompt automatically, and the confirmation would be just another flag. So a non-interactive stdin is refused even with --force.

Anything other than the exact environment name aborts before a single table is dropped.

The same gate applies to your application binary's own subcommand (./app migrate:fresh --force), which is the one a production deploy actually runs.

db:sync

Regenerate the SeaORM entity files in src/models/entities/ from the current database schema, and (when a src/bin/migrate.rs exists) run pending migrations first.

suprnova db:sync [--skip-migrations] [--regenerate-models]
Option Description
--skip-migrations Skip the migration pass and only regenerate entities
--regenerate-models Overwrite src/models/<table>.rs files too, not just src/models/entities/<table>.rs

What it does

  1. (Optional) Runs pending migrations. The default scaffold does not ship a src/bin/migrate.rs, so this step is a no-op and prints Migration binary not found, skipping migrations. In a default project, run suprnova migrate first, then suprnova db:sync --skip-migrations.
  2. Connects to DATABASE_URL, introspects every user table (skipping seaql_migrations and any name starting with _), and writes one entity file per table to src/models/entities/<table>.rs.
  3. Writes a thin user-facing model file at src/models/<table>.rs - but only if that file does not already exist, so your hand-written accessors, scopes, and observer hooks survive.
  4. --regenerate-models overrides the protection in step 3 and overwrites those user files. Use it when you have not customised them yet, or when you have a backup.

Typical workflow

# 1. Author a migration
suprnova make:migration create_posts_table
# (edit src/migrations/m..._create_posts_table.rs)

# 2. Apply it
suprnova migrate

# 3. Regenerate the entities so the new table is reachable from code
suprnova db:sync --skip-migrations

Why Suprnova diverges

Laravel has one global artisan that owns every framework command, including db:seed. Suprnova splits this in two:

  • The suprnova developer CLI (this chapter) owns project scaffolding, generators, and the migration commands. It is installed once per developer machine via cargo install and shells into your app binary to do work that needs the app's Migrator.
  • A per-project console binary, built from your project's src/bin/console.rs, owns db:seed, your #[command]-annotated handlers, queue:work, schedule:run, workflow:work, and other one-shot tasks that need your app's bootstrap, container bindings, and registered observers.

Migration commands live on the developer CLI because they have a deterministic shape that does not depend on your bootstrap. Everything that needs your service container or your registered seeders lives on the per-project console binary. See Console for the full console surface.

db:seed

Not a suprnova CLI command. Run seeders through the per-project console binary:

cargo run --bin console -- db:seed
cargo run --bin console -- db:seed --class=UsersSeeder

The seeder registry, ordering rules, and the --class matching are covered in Seeding. The framework ships db:seed as a built-in console command - your scaffold gets it without any wiring on your side, but you do invoke it through console, not through suprnova.

Summary

Command What it does
suprnova make:migration <name> Scaffold a new migration file and register it in Migrator
suprnova migrate Run pending migrations
suprnova migrate:status Show applied/pending status
suprnova migrate:rollback [--step N] Roll back the last N migrations (default 1)
suprnova migrate:fresh Drop all tables and re-run every migration
suprnova db:sync [--skip-migrations] [--regenerate-models] Regenerate SeaORM entities from the live schema
cargo run --bin console -- db:seed Run registered seeders (per-project console, not the suprnova CLI)

Next

  • Migrations - schema-builder API: tables, columns, indexes, foreign keys
  • Seeding - authoring seeders and the db:seed console command
  • Console - the per-project console binary and #[command] handlers
  • Database - connections, drivers, transactions, the query builder
  • CLI Overview - every suprnova subcommand at a glance