Quick Start
Create and run your first database migration with go-migration in minutes.
Quick Start
This guide walks you through creating and running your first migration with go-migration v1.2.0. By the end, you'll have a users table in your database.
Initialize Your Project
The fastest way to get started is with go-migration init. This command scaffolds all the directories and files you need:
go-migration initThis creates:
database/migrations/,database/seeders/,database/factories/directoriescmd/migrator/main.go— entry point that callsapp.Run()(fromgithub.com/gopackx/go-migration/pkg/cli/app)migration.json— config file with environment variable placeholders (JSON format)
The command auto-detects your module path from go.mod. See the CLI Reference for flags like --module and --force.
If you prefer to set up manually, you can skip this step and create the config file and directories yourself — see the steps below.
Create a Config File
If you ran go-migration init, a migration.json was already created in your project root with sensible defaults. Open it and fill in your database credentials:
{
"default": "postgres",
"connections": {
"postgres": {
"driver": "postgres",
"host": "${DB_HOST}",
"port": 5432,
"database": "${DB_NAME}",
"username": "${DB_USER}",
"password": "${DB_PASSWORD}",
"sslmode": "disable"
}
},
"migration_dir": "database/migrations",
"seeder_dir": "database/seeders",
"factory_dir": "database/factories",
"migration_table": "migrations",
"log_level": "info"
}If you prefer YAML, create a go-migration.yaml manually. Note: YAML format is deprecated since v1.0.0 and will show a deprecation warning when loaded.
default: postgres
connections:
postgres:
driver: postgres
host: ${DB_HOST}
port: 5432
database: ${DB_NAME}
username: ${DB_USER}
password: ${DB_PASSWORD}
sslmode: disable
migration_dir: database/migrations
seeder_dir: database/seeders
factory_dir: database/factories
migration_table: migrations
log_level: info${VAR_NAME} placeholders are replaced with environment variable values at load time. See Configuration for all supported fields.
Define a Migration
Create a migration file with an init() function for auto-registration, plus Up and Down methods:
package migrations
import (
"github.com/gopackx/go-migration/pkg/migrator"
"github.com/gopackx/go-migration/pkg/schema"
)
func init() {
migrator.AutoRegister("2025_01_15_143022_1234_create_users_table", &CreateUsersTable{})
}
type CreateUsersTable struct{}
func (m *CreateUsersTable) Up(s *schema.Builder) error {
return s.Create("users", func(bp *schema.Blueprint) {
bp.ID()
bp.String("name", 255)
bp.String("email", 255).Unique()
bp.Boolean("active").Default(true)
bp.Timestamps()
})
}
func (m *CreateUsersTable) Down(s *schema.Builder) error {
return s.Drop("users")
}The init() function registers the migration automatically when the package is imported. The Up method creates the table, and Down reverses it.
Migration filenames in v1.0.0 use the format YYYY_MM_DD_HHMMSS_RRRR_description.go, where RRRR is a random 4-digit number to prevent timestamp collisions. The make:migration command generates this automatically. The old format (YYYYMMDDHHMMSS_description.go) is still supported.
Set Up and Run
app.Run() is the all-in-one CLI runner. It handles config loading, database connection, auto-discovery of migrations and seeders, grammar auto-resolution, and command dispatch — all in a single call. This is exactly what go-migration init generates in cmd/migrator/main.go:
package main
import (
"github.com/gopackx/go-migration/pkg/cli/app"
_ "github.com/go-sql-driver/mysql"
_ "github.com/lib/pq"
_ "modernc.org/sqlite"
_ "your-project/database/migrations" // blank import triggers init() registration
_ "your-project/database/seeders" // blank import triggers seeder init() registration
)
func main() {
app.Run()
}The three driver imports register the database/sql drivers; keep only the ones you use.
Upgrading from v1.1.x: migrator.Run() was removed in v1.2.0. Replace the import github.com/gopackx/go-migration/pkg/migrator with github.com/gopackx/go-migration/pkg/cli/app, call app.Run() instead of migrator.Run(), and replace _ "github.com/mattn/go-sqlite3" with _ "modernc.org/sqlite". The CLI now lives in its own package, so importing pkg/migrator as a library no longer pulls in the CLI, the config loader, or the YAML parser. See the Changelog.
That's it. app.Run() will:
- Parse CLI arguments (
migrate,db:seed,migrate:fresh, etc.) - Load
migration.json(with${ENV}interpolation, falls back togo-migration.yaml) - Open a database connection
- Auto-discover all registered migrations and seeders
- Resolve the correct grammar from the driver name
- Execute the requested command
If you need more control, you can set up the migrator manually:
package main
import (
"database/sql"
"log"
_ "github.com/lib/pq" // PostgreSQL driver
"github.com/gopackx/go-migration/pkg/migrator"
"github.com/gopackx/go-migration/pkg/schema/grammars"
_ "your-project/database/migrations" // blank import triggers init() registration
)
func main() {
db, err := sql.Open("postgres", "postgres://user:password@localhost:5432/mydb?sslmode=disable")
if err != nil {
log.Fatal(err)
}
defer db.Close()
m := migrator.New(db,
migrator.WithGrammar(grammars.NewPostgresGrammar()),
migrator.WithAutoDiscover(),
)
if err := m.Up(); err != nil {
log.Fatal(err)
}
log.Println("Migrations completed successfully!")
}The blank import _ "your-project/database/migrations" triggers all init() functions, and WithAutoDiscover() loads them into the migrator.
Run It
# With app.Run() — use CLI commands directly
go run ./cmd/migrator migrate
# Or run all pending migrations with manual setup
go run main.gogo-migration will:
- Take the cross-process migration lock (see Concurrency and Locking)
- Create a
migrationstracking table (if it doesn't exist) - Check which migrations have already run
- Execute the
Upmethod of any pending migrations - Record each migration in the tracking table with a batch number, inside the migration's own transaction
Replace the connection string or environment variables with your actual database credentials. The example above uses PostgreSQL — see Database Grammars for MySQL and SQLite setup.
Using app.Run()
With app.Run(), your binary becomes a full CLI tool. All standard commands are available:
go run ./cmd/migrator migrate # Run pending migrations
go run ./cmd/migrator migrate --dry-run # Preview SQL without applying (run migrate:install first)
go run ./cmd/migrator migrate:rollback # Roll back last batch
go run ./cmd/migrator migrate:fresh # Drop all tables + re-migrate
go run ./cmd/migrator migrate:refresh # Reset + re-migrate
go run ./cmd/migrator migrate:status # Show migration status (read-only)
go run ./cmd/migrator migrate:status --check # Exit 1 if anything is pending
go run ./cmd/migrator db:seed # Run all seeders
go run ./cmd/migrator db:seed --class=UserSeeder # Run specific seeder
go run ./cmd/migrator db:seed --tag=core # Run seeders by tag
go run ./cmd/migrator make:migration create_posts # Scaffold a migration file (lower snake_case name)app.Run() uses smart command classification — commands like help, init, and make:* skip database setup entirely, so they work even without a valid database connection.
New in v1.0.0: the --tag flag on db:seed for running seeders by tag, the --force flag on migrate:reset, the --dry-run flag on the migrate commands, and the db:seed:rollback and db:seed:truncate commands. New in v1.2.0: --lock-timeout and --no-lock on every command that writes, and --check on migrate:status. See the CLI Reference for all available flags.
Using a Different Database
go-migration supports PostgreSQL, MySQL, and SQLite. With app.Run(), the grammar is auto-resolved from the driver field in your config file — no manual grammar selection needed.
For manual setup, swap the driver import and grammar:
import (
"database/sql"
_ "github.com/go-sql-driver/mysql"
"github.com/gopackx/go-migration/pkg/migrator"
"github.com/gopackx/go-migration/pkg/schema/grammars"
_ "your-project/database/migrations"
)
func main() {
db, _ := sql.Open("mysql", "user:password@tcp(localhost:3306)/mydb")
m := migrator.New(db,
migrator.WithGrammar(&grammars.MySQLGrammar{}),
migrator.WithAutoDiscover(),
)
// Run as before...
}import (
"database/sql"
_ "modernc.org/sqlite" // pure Go, registers as "sqlite"
"github.com/gopackx/go-migration/pkg/migrator"
"github.com/gopackx/go-migration/pkg/schema/grammars"
_ "your-project/database/migrations"
)
func main() {
db, _ := sql.Open("sqlite", "./app.db")
m := migrator.New(db,
migrator.WithGrammar(&grammars.SQLiteGrammar{}),
migrator.WithAutoDiscover(),
)
// Run as before...
}Rolling Back
To undo the last batch of migrations:
// Roll back the last batch
if err := m.Rollback(0); err != nil {
log.Fatal(err)
}This calls the Down method on each migration in the last batch, in reverse order.
What's Next?
- Building Your CLI — build a project binary to run migrations, seeders, and factories (like Laravel Artisan)
- Project Structure — recommended directory layout
- Migrations — deep dive into defining and managing migrations
- Schema Builder — learn the full table and column API