CLI tool
pressly/goose avatar
pressly/goose

goose: SQL Migration Annotations, Transaction Rules and the Go Migration Catch

A database migration tool. Supports SQL migrations and Go functions.

11,689 stars727 forksGoNOASSERTION

At a glance

What is it?
goose is a Go database migration tool that ships as a CLI and a library, running SQL files with Up and Down annotations or Go functions. Its details live in the annotations, the transaction defaults, and the fact that Go migrations need your own binary.
Who is it for?
goose is a solid pick for teams that want plain SQL migrations with a status table and the option to drop into Go when SQL is not enough. The cost is a handful of rules you must follow exactly: one Up annotation per file, semicolons on every statement, MySQL connection flags, and a custom-built binary for Go migrations.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 2 days ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on November 14, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • pressly/goose11,689 ★ · +201
Star history
Repository2026-09-222026-11-14
pressly/goose11.5k11.7k

201 Stars in the last 53 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

Each SQL file needs exactly one Up annotation, placed before Down

goose migrations are ordinary SQL files with special comments. The README sample shows the whole contract:

sql
-- +goose Up
CREATE TABLE post (
    id int NOT NULL,
    title text,
    body text,
    PRIMARY KEY(id)
);

-- +goose Down
DROP TABLE post;

Each migration file must have exactly one `-- +goose Up` annotation. The `-- +goose Down` annotation is optional. If a file has both, the Up annotation must come first. Statements after Up run as the forward migration, and statements after Down run as the rollback.

Two consequences follow. A migration with no Down section can be applied but cannot be rolled back, so `goose down` has nothing to run for it. And a file with a missing or duplicated Up annotation is malformed. Because the annotations are comments, your editor and database client will treat the file as valid SQL, and the mistake shows up when goose reads it, not when you write it.

Migrations run in transactions by default, except where you opt out per file

By default all migrations run within a transaction. Some statements, such as `CREATE DATABASE`, cannot be run inside one. For those you add `-- +goose NO TRANSACTION` to the top of the migration file, and both the Up and Down parts of that file then run without transactions.

The trade-off is plain. Inside a transaction, a failure part-way can roll the change back. With NO TRANSACTION, a failure part-way can leave the database half migrated, and you have to repair it by hand. Keep such files to a single statement where possible.

Statement parsing has its own rules. By default, SQL statements are delimited by semicolons, and query statements must end with a semicolon to be recognised by goose. Statements with semicolons inside them, such as PL/pgSQL bodies, must be wrapped in `-- +goose StatementBegin` and `-- +goose StatementEnd` annotations. A function definition without them will be split at the first inner semicolon, so write the markers whenever you create functions or triggers.

MySQL and MariaDB need two connection flags or status and multi-statement files break

The README attaches two notes to the mysql driver, which covers both MySQL and MariaDB. First, the `parseTime` flag must be enabled, a note placed under the status command. The status output prints the time each migration was applied, and that needs time values parsed.

Second, `multiStatements` must be enabled. It is required when you write multiple queries separated by semicolons in a single SQL file. Without it, a normal goose migration with two statements is rejected by the go-sql-driver/mysql connection.

Both settings belong in your connection string, which means they have to be present anywhere the string is built: your local environment, CI and production. The other drivers listed in the help output are postgres, sqlite3, spanner, mssql, azuresql, redshift, tidb, clickhouse, ydb, starrocks and turso. The README keeps driver specifics in a separate section, so read it for the database you actually run, not just the general usage.

Connection settings can come from GOOSE_ variables or a .env file that loads by default

Instead of passing a driver and connection string on every call, you can set environment variables:

shell
export GOOSE_DRIVER=DRIVER
export GOOSE_DBSTRING=DBSTRING
export GOOSE_MIGRATION_DIR=MIGRATION_DIR
export GOOSE_TABLE=TABLENAME

The other route is a `.env` file with the same keys. The README example uses `GOOSE_DRIVER=postgres`, a `GOOSE_DBSTRING` pointing at a local Postgres on port 5432, `GOOSE_MIGRATION_DIR=./migrations` and `GOOSE_TABLE=custom.goose_migrations`.

Loading from `.env` files is enabled by default. That is convenient on a laptop and a hazard in a build directory that contains a stray `.env` pointing somewhere unexpected. To disable the feature, set the `-env=none` flag, and to load a specific file, pass its path to `-env`.

The table setting also controls the schema. By default all migrations run on the public schema, and to use another one you give the schema name in the table option, as in `-table='schemaname.goose_db_version`. goose keeps its version history in that table, so changing the name on an existing database makes goose see no history.

Go migrations require your own goose binary, not the stock one

goose supports migrations written in SQL or in Go. The create command shows both: `goose create add_some_column sql` writes a timestamped SQL file such as 20170506082420_add_some_column.sql, and `goose create fetch_user_data go` writes a Go file named 20170506082421_fetch_user_data.go.

The catch is in one clause of the README: a Go migration can be created, but then invoked only with your own goose binary. The standard `goose` executable you installed does not contain your Go functions, so you compile a binary that links in your migrations and run that. Teams that choose Go migrations therefore take on a small build and deploy step that pure-SQL teams skip.

There is also a sequential numbering option. `goose -s create add_some_column sql` creates 00001_add_some_column.sql in place of a timestamp. Timestamp names avoid collisions between branches, while sequential names read more cleanly but can clash when two developers each create the next number. The feature list also mentions embedded migrations, out-of-order migrations, seeding data and environment variable substitution in SQL migrations.

The default binary links every database driver, and build tags strip the ones you skip

The install command is a single line:

shell
go install github.com/pressly/goose/v3/cmd/goose@latest

It puts the `goose` binary in your `$GOPATH/bin` directory. Because the tool supports Postgres, MySQL, MariaDB, Spanner, SQLite, YDB, ClickHouse, MSSQL, Vertica and more, the default binary includes many drivers. To shrink it, build a lite version with tags:

shell
go build -tags='no_postgres no_mysql no_sqlite3 no_ydb' -o goose ./cmd/goose

The available tags are no_azuresql, no_clickhouse, no_libsql, no_mssql, no_mysql, no_postgres, no_sqlite3, no_vertica and no_ydb, and no_mssql also excludes the azuresql driver. Excluding a driver you later need gives you a binary that cannot talk to that database. On macOS there is a Homebrew formula, `brew install goose`.

Building from source needs a recent toolchain: go.mod declares `go 1.26.0`. The module path is github.com/pressly/goose/v3, so library imports include the v3 suffix. The Makefile also includes a `dist` target that cross-builds for darwin, linux and windows.

Rolling back to zero is one command, so status and up-by-one deserve more use

The command set is small: create, up, up-to, up-by-one, down, down-to, status and version. `goose up` applies every pending migration and prints an OK line for each file. `goose up-to 20170506082420` stops at a chosen version, and `goose up-by-one` applies a single migration, which is a safer way to walk through a risky change.

On the way back, `goose down` rolls back one migration and `goose down-to 20170506082527` rolls back to a version. The README then shows `goose down-to 0` for rolling back all migrations, and it adds the single word careful in parentheses. Against a production database, that command runs every Down section you have written.

The habit worth building is to run `goose status` first. It prints each migration with its applied time, or marks it Pending, and `goose version` prints the current version. Status is the quick check that your environment variables point at the database you think they do, before any of the destructive commands run.

Releases are frequent, and GitHub shows the license as NOASSERTION

Recent releases are v3.29.0 on 2026-10-11, v3.28.0 on 2026-09-02 and v3.27.3 on 2026-07-22, and the last push was on 2026-10-31. A CHANGELOG.md sits at the top of the repository, and the source tree includes provider files such as provider.go and provider_run.go next to the CLI code, so the library exposes more than the command line.

One gap affects anyone with a compliance process. GitHub's license field reads NOASSERTION, meaning it did not detect a standard license. There is a LICENSE file in the repository, so read that file directly and have it reviewed before you rely on any assumption about the terms.

The repository's tests use a Makefile with database ports for Postgres, MySQL, ClickHouse, YDB, Turso and StarRocks, and they pull in Docker client libraries per go.mod. That is a sign the project is tested against real servers, but the README makes no statement about which database versions are supported, so confirm your version with a throwaway database before trusting a migration plan.

Editorial conclusion

goose is a solid pick for teams that want plain SQL migrations with a status table and the option to drop into Go when SQL is not enough. The cost is a handful of rules you must follow exactly: one Up annotation per file, semicolons on every statement, MySQL connection flags, and a custom-built binary for Go migrations. Check the LICENSE file yourself, since GitHub reports NOASSERTION.

Frequently asked questions

What is goose?

goose is a database migration tool that works as both a CLI and a library. You manage your database schema with incremental SQL changes or Go functions.

How to install goose?

Run go install github.com/pressly/goose/v3/cmd/goose@latest, which places the binary in your GOPATH bin directory. On macOS you can use brew install goose.

How to use goose?

Create a migration with goose create add_some_column sql, edit the file with Up and Down annotations, then run goose up. Use goose status to see which migrations are applied or pending.

Official sources

  1. Issues
  2. pressly/goose on GitHub
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/pressly-goose.svg)](https://hysenlabs.com/projects/pressly-goose)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/pressly-goose.svg)](https://hysenlabs.com/projects/pressly-goose)

Was this useful?