Migrations Introduction

Migrations provide version control for your database schema. Instead of manually altering tables, you define Up and Down functions that apply and revert changes. Migrations run in order and are tracked in a schema_migrations table.

Why migrations?

Migrations let you:

  • Evolve the schema over time without losing data
  • Share schema changes across the team via version control
  • Roll back mistakes with Down
  • Deploy schema updates consistently to staging and production

Creating a migration

Generate a migration with the CLI:

nimbus make:migration create_posts

This creates a timestamped file in database/migrations/. The timestamp (e.g. 20260308120000) ensures migrations run in chronological order.

Migration structure

Each migration implements database.Migration:

type Migration struct {
    Name string
    Up   func(*gorm.DB) error
    Down func(*gorm.DB) error
}

Register migrations in database/migrations/registry.go and pass them to NewMigrator.

Running migrations

migrator := database.NewMigrator(db, migrations.All())
if err := migrator.Up(); err != nil {
    log.Fatalf("migration failed: %v", err)
}

Run nimbus db:migrate from your app root, or invoke go run . migrate if your main.go wires it.

Rollback

Down() reverses the last applied migration.

migrator.Down()

Best practices

  • Always implement both Up and Down
  • Use timestamp prefixes for deterministic ordering
  • One logical change per migration
  • Never edit applied migrations — add a new one instead