Lightweight database migration library for Go.
Define migrations in Go, not SQL files.
- Migrations as Go code — type-safe structs validated at compile time, no separate
.sqlfiles to ship. - SQL or Go functions — pure SQL for schema, Go functions for data transformations. Both run inside a transaction.
- 6 databases — PostgreSQL, MySQL, SQLite, ClickHouse, CockroachDB, MS SQL Server.
- Embeddable CLI + TUI — wire the CLI into your own binary alongside your migrations (see CLI).
- Distributed locking — safe to run from multiple instances, with configurable lock timeout.
- Checksum validation — SHA-256 over SQL detects modifications to already-applied migrations.
- Gap detection — finds missing, skipped, or unregistered migrations.
- Dry run / plan / explain — preview what will run before touching the database.
- Naming patterns — optional regex enforcement for migration versions.
- Configurable isolation — set transaction isolation level globally or per migration.
- Rich metadata — records who applied each migration, when, on which host, in which environment, and how long it took.
- Migration toolkit —
squash,baseline, andimportcommands to consolidate history or onboard an existing database.
go get github.com/honeynil/queenRequires Go 1.24+.
package main
import (
"context"
"database/sql"
"log"
"github.com/honeynil/queen"
"github.com/honeynil/queen/drivers/postgres"
_ "github.com/jackc/pgx/v5/stdlib"
)
func main() {
db, err := sql.Open("pgx", "postgres://localhost/myapp?sslmode=disable")
if err != nil {
log.Fatal(err)
}
defer db.Close()
q := queen.New(postgres.New(db))
defer q.Close()
q.MustAdd(queen.M{
Version: "001",
Name: "create_users_table",
UpSQL: `
CREATE TABLE users (
id SERIAL PRIMARY KEY,
email VARCHAR(255) NOT NULL UNIQUE,
name VARCHAR(255),
created_at TIMESTAMP DEFAULT NOW()
)
`,
DownSQL: `DROP TABLE users`,
})
if err := q.Up(context.Background()); err != nil {
log.Fatal(err)
}
}When a change is more than schema, register a Go function instead of (or alongside) SQL. It runs inside the same transaction:
q.MustAdd(queen.M{
Version: "002",
Name: "normalize_emails",
ManualChecksum: "v1", // bump when the function logic changes
UpFunc: func(ctx context.Context, tx *sql.Tx) error {
_, err := tx.ExecContext(ctx,
`UPDATE users SET email = LOWER(TRIM(email))`)
return err
},
})UpSQL and UpFunc can be combined in a single migration.
Because migrations are Go code, Queen's CLI is shipped as a library you embed in your own main.go together with your migrations. This way the CLI always knows about the exact set of migrations your application ships with — no separate registry, no file scanning.
// cmd/migrate/main.go
package main
import (
"github.com/honeynil/queen"
"github.com/honeynil/queen/cli"
)
func main() {
cli.Run(func(q *queen.Queen) {
q.MustAdd(queen.M{Version: "001", Name: "create_users", UpSQL: `...`, DownSQL: `...`})
q.MustAdd(queen.M{Version: "002", Name: "add_index", UpSQL: `...`, DownSQL: `...`})
})
}Then run any command against your project:
go run ./cmd/migrate up --driver postgres --dsn "postgres://localhost/myapp?sslmode=disable"
go run ./cmd/migrate status
go run ./cmd/migrate planAvailable commands: up, down, reset, goto, status, log, plan, explain, validate, check, gap, diff, doctor, create, init, squash, baseline, import, tui.
Configuration can also come from a .queen.yaml file (--use-config), with per-environment settings (--env production).
Inspect what Up or Down will do without applying anything:
plans, _ := q.DryRun(ctx, queen.DirectionUp, 0)
for _, p := range plans {
fmt.Printf("%s %s [%s] destructive=%v warnings=%v\n",
p.Version, p.Name, p.Type, p.IsDestructive, p.Warnings)
}For each applied migration record, Queen can persist execution metadata alongside version, name, checksum, and timestamp. The built-in drivers support these fields:
applied_by— current OS user when availableduration_ms— migration execution time in millisecondshostname— current machine hostname when availableenvironment— value ofQUEEN_ENVaction— operation type such asapplyormark-appliedstatus— operation result such assuccesserror_message— optional error details when recorded by the driver flow
This table represents the current applied state of migrations. It is not a full append-only audit log of every migration event.
Use Status() when you need the current state in code, and Driver().GetApplied() when you want the persisted applied records including metadata.
Custom database drivers implement the queen.Driver interface:
type Driver interface {
Init(ctx context.Context) error
GetApplied(ctx context.Context) ([]Applied, error)
Record(ctx context.Context, m *Migration, meta *MigrationMetadata) error
Remove(ctx context.Context, version string) error
Lock(ctx context.Context, timeout time.Duration) error
Unlock(ctx context.Context) error
Exec(ctx context.Context, isolationLevel sql.IsolationLevel, fn func(*sql.Tx) error) error
Close() error
}In practice:
Initprepares the migration tracking tables or any driver-specific stateGetAppliedreturns the persisted applied migration recordsRecordpersists a successful applied migration together with optional metadataRemoveremoves an applied migration record after rollbackLockandUnlockprovide distributed migration safetyExecruns migration code inside a transaction with the requested isolation levelClosereleases driver resources
If you are implementing a custom driver, use the built-in drivers as the reference behavior for locking, metadata persistence, and transaction execution.
When multiple instances of your app or CI run migrations concurrently, Queen acquires a database-level lock so only one runs at a time:
q := queen.NewWithConfig(driver, &queen.Config{
TableName: "queen_migrations",
LockTimeout: 10 * time.Minute,
})Set SkipLock: true for single-instance setups or local development.
| Database | Driver | SQL Driver |
|---|---|---|
| PostgreSQL | queen/drivers/postgres |
github.com/jackc/pgx/v5/stdlib |
| MySQL | queen/drivers/mysql |
github.com/go-sql-driver/mysql |
| SQLite | queen/drivers/sqlite |
github.com/mattn/go-sqlite3 |
| ClickHouse | queen/drivers/clickhouse |
github.com/ClickHouse/clickhouse-go/v2 |
| CockroachDB | queen/drivers/cockroachdb |
github.com/jackc/pgx/v5/stdlib |
| MSSQL | queen/drivers/mssql |
github.com/microsoft/go-mssqldb |
Full documentation: queen-wiki.honeynil.tech (in progress)
Apache License 2.0
