goose
A database migration tool for Go, driven by SQL files or plain Go functions, with a CLI and an embeddable provider library.
Repository Health
Technical Analysis
Goose is a database schema migration tool built in Go. It ships both as a standalone CLI binary and as an importable library (github.com/pressly/goose/v3) centered on the Provider type, so teams can run migrations from a terminal during deploys or embed migration execution directly inside a Go application’s startup path.
Migrations can be written as versioned .sql files with -- +goose Up / -- +goose Down annotations, or as plain Go functions registered at compile time — useful for data backfills or logic too complex for SQL. Goose supports out-of-order migration application, environment-variable substitution inside SQL files, and running migrations from an embed.FS so a single compiled binary can carry its own migration set.
It ships first-class dialect support for Postgres, MySQL/MariaDB, SQLite, ClickHouse, MSSQL, Vertica, YDB, Spanner, Redshift, TiDB, StarRocks, and Turso/libSQL, with build tags available to trim unused drivers from the compiled binary.
What You Get
- A
gooseCLI binary withup,up-to,down,down-to,redo,reset,status,version,create,fix, andvalidatesubcommands for driving migrations from the terminal or CI - An importable
Providertype (goose.NewProvider) for running and inspecting migrations programmatically from inside a Go application, backed bydatabase/sql - SQL migrations with
-- +goose Up/-- +goose Downannotations, transaction control via-- +goose NO TRANSACTION, and multi-statement blocks viaStatementBegin/StatementEnd - Go migrations written as plain registered functions for logic too complex to express in SQL, including no-transaction variants
- Support for reading migrations from an
embed.FSso a compiled binary can ship its own migration set with no external files at runtime - Built-in dialect implementations for Postgres, MySQL/MariaDB, SQLite, ClickHouse, MSSQL, Vertica, YDB, Spanner, Redshift, TiDB, StarRocks, and Turso/libSQL, each satisfying a common
database.Storeinterface - Environment-variable substitution inside SQL migration files, scoped with
-- +goose ENVSUB ON/OFFannotations
Common Use Cases
- Running versioned schema migrations as a discrete step in a CI/CD deploy pipeline via the
gooseCLI - Embedding migration execution inside a Go service’s own startup code using the
ProviderAPI, so the binary self-migrates on boot - Shipping a single self-contained binary that carries its migrations via
embed.FS, with no separate migration directory to deploy alongside it - Backfilling or transforming data during a schema change using a Go-function migration instead of hand-written SQL
- Supporting multiple target databases (e.g. Postgres in production, SQLite in tests) from the same migration set through goose’s dialect abstraction
Under The Hood
Architecture
The library is centered on the Provider type in provider.go, constructed via NewProvider(dialect, db, fsys, opts...); it owns a *sql.DB, a StoreController (from internal/controller) that wraps a dialect-specific database.Store implementation, and an ordered, immutable slice of collected *Migration values built by provider_collect.go from the supplied fs.FS. Execution flows through provider_run.go, which resolves target versions, opens transactions per-dialect via the store, and applies either SQL statements (parsed by internal/sqlparser) or registered Go functions (tracked via register.go/globals.go); a sync.Mutex on Provider serializes concurrent operations. Dialect-specific behavior is isolated behind the database.Store interface in database/store.go, with per-database implementations under internal/dialects, so adding a new target database is a matter of implementing that interface rather than touching core migration logic. The CLI in cmd/goose is a thin wrapper that constructs a Provider from flags/env vars and calls the same public API application code would use, meaning the CLI and library paths share one execution engine end to end — what breaks the core Provider/Store contract breaks both surfaces identically.
Tech Stack
Written in Go 1.25 with zero required third-party dependencies for its core engine — database/sql is the only interface it targets directly. Each optional dialect pulls in its own driver as a separate dependency (github.com/jackc/pgx/v5 for Postgres, github.com/go-sql-driver/mysql, modernc.org/sqlite for pure-Go SQLite, github.com/ClickHouse/clickhouse-go/v2, github.com/microsoft/go-mssqldb, github.com/vertica/vertica-sql-go, github.com/ydb-platform/ydb-go-sdk/v3, github.com/tursodatabase/libsql-client-go), and these can be excluded from a compiled binary via Go build tags (no_postgres, no_mysql, etc.) to shrink the CLI binary. go.uber.org/multierr aggregates errors across statements, and github.com/sethvargo/go-retry backs retry logic. Releases are automated with GoReleaser (.goreleaser.yaml), and the CLI is distributed via go install, a Homebrew formula, and a shell installer.
Code Quality
The repository carries roughly two dozen _test.go files alongside their corresponding implementation files (provider_run_test.go, provider_collect_test.go, migrate_test.go, fix_test.go, etc.), using testify for assertions per testifylint lint rules, plus a dedicated tests/ directory and Docker-backed integration testing (pkg/dockermanage, .github/workflows/integration.yaml) that exercises real database engines rather than mocks alone. .golangci.yaml enables errcheck, govet, ineffassign, staticcheck, misspell, testifylint, and unused, run through a dedicated lint CI workflow, and a separate release workflow gates tagged builds. Errors are wrapped and returned explicitly throughout rather than swallowed, and package-level doc comments describe concurrency guarantees (e.g. Provider methods being safe for concurrent use unless noted).
API Design
The public API is deliberately small and idiomatic: NewProvider returns a single *Provider value configured via functional options (ProviderOption), and callers interact through a handful of clearly named methods (Up, UpTo, Down, DownTo, Status) that mirror the CLI’s own subcommands one-to-one, so developers moving between CLI usage and library usage face no conceptual translation. Extensibility points — a custom database.Store, a custom migrations table name, a filtered fs.Sub filesystem — are exposed through the same options mechanism rather than special-cased flags, keeping the surface area consistent as new capabilities are added. Documentation is dense: exported types carry Go-doc comments explaining not just what a function does but which combinations of arguments are valid, reducing the trial-and-error typically needed to adopt a new migration tool.
Used by 7 apps in this directory
Beta9
Developer Tools · AI Development · Data Engineering
Run AI workloads at scale with a Pythonic serverless runtime that handles GPU inference, background jobs, and sandboxes with zero infrastructure overhead.
Crush
Developer Tools · AI Code Assistants · AI Assistants
Your terminal coding companion — wire up any LLM with LSP intelligence, MCP extensibility, and a skills system that learns your workflow.
CubeSandbox
Developer Tools · Security · AI Agents
Instant, concurrent, hardware-isolated MicroVM sandboxes for AI agents — E2B-API compatible, sub-60ms cold starts, and a built-in zero-trust egress proxy, all self-hostable at scale.
Grafana
Monitoring · Analytics
The open-source observability platform that unifies metrics, logs, and traces from any data source into dynamic, queryable dashboards.
Hatchet
AI Development · Developer Tools · Automation
A Postgres-backed orchestration engine for background tasks, AI agents, and durable workflows that replaces Redis queues and multi-datastore durable execution platforms with a single self-hostable service.
HyperDX
Developer Tools · Analytics · Monitoring
Open source observability platform that unifies logs, traces, metrics, and session replays on ClickHouse — now the core of ClickStack.
Navidrome
File Storage
Run your own personal Spotify — stream your entire music collection from any device, anywhere, forever.