Hi there! I'm Shrijith Venkatrama, founder of Hexmos. Right now, I’m buildingLiveAPI, a first of its kind tool for helping you automatically index API endpoints across all your repositories. LiveAPI helps you discover, understand and use APIs in large tech infrastructures with ease.
Database migrations are a critical part of building and maintaining Go applications. They keep your database schema in sync with your codebase, handle updates, and ensure your app stays reliable as it evolves. Choosing the right migration tool can save you time, reduce errors, and make deployments smoother. In this post, we’ll dive into thebest database migration tools for Go, with examples, comparisons, and practical insights to help you pick the right one for your project.
I’ve been through the grind of manual migrations and the chaos of mismatched schemas, so I’ll break down each tool’s strengths, quirks, and use cases in a way that’s easy to follow. Let’s explore the top options, complete with code examples you can actually run.
Why Database Migrations Matter in Go
Before we jump into the tools, let’s talk about why migrations are a big deal. In Go projects, your database schema often evolves—new tables, updated columns, or index changes. Without a migration tool, you’re stuck writing raw SQL, manually tracking versions, or praying your team doesn’t mess up the production database. A good migration toolautomates schema changes,tracks history, and ensures consistency across environments.
The tools we’ll cover work well with Go’s ecosystem, integrate with popular databases like PostgreSQL and MySQL, and focus on simplicity or flexibility depending on your needs. Let’s dive into the first tool.
1. Goose: Simple and Lightweight Migrations
Goose is a no-fuss, lightweight migration tool for Go. It’s perfect for developers who wantminimal setup andSQL-based migrations without heavy dependencies. Goose supports PostgreSQL, MySQL, SQLite, and more, and it’s easy to integrate into a Go project.
Key Features
- SQL or Go-based migrations: Write migrations in raw SQL or Go code.
- CLI-driven: Run migrations with simple commands like
goose up
orgoose down
. - No external dependencies: Just a Go binary and your database driver.
Example: Creating a User Table with Goose
Here’s how you can set up a migration to create ausers
table in PostgreSQL.
First, install Goose:
go get-u github.com/pressly/goose/v3
Create a migration file (e.g.,20250607101700_create_users_table.sql
):
-- +goose UpCREATETABLEusers(idSERIALPRIMARYKEY,usernameVARCHAR(50)NOTNULL,emailVARCHAR(100)NOTNULLUNIQUE,created_atTIMESTAMPDEFAULTCURRENT_TIMESTAMP);-- +goose DownDROPTABLEusers;
Run the migration:
goose-dir migrations postgres"user=postgres password=secret dbname=mydb sslmode=disable" up
Output: Theusers
table is created in your PostgreSQL database. Runninggoose down
will drop it.
When to Use Goose
Goose shines for small to medium projects where you wantfull control over SQL and a lightweight tool. It’s not ideal for complex migrations requiring programmatic logic, as its Go-based migrations can feel clunky compared to other tools.
2. Migrate: The CLI Powerhouse
Migrate is another popular choice for Go developers. It’s a CLI-first tool that supports a wide range of databases (PostgreSQL, MySQL, SQLite, etc.) and focuses onsimplicity and portability. Unlike Goose, Migrate is language-agnostic, so it’s great for teams using multiple languages alongside Go.
Key Features
- Broad database support: Works with almost any database, including cloud-native ones like CockroachDB.
- File-based migrations: Uses plain SQL files with up/down scripts.
- CLI focus: No Go code required, making it easy to use in CI/CD pipelines.
Example: Adding a Posts Table with Migrate
Install Migrate:
go get-u github.com/golang-migrate/migrate/v4
Create a migration file (e.g.,20250607101800_create_posts_table.sql
):
-- +upCREATETABLEposts(idSERIALPRIMARYKEY,user_idINTEGERREFERENCESusers(id),titleVARCHAR(255)NOTNULL,contentTEXT,created_atTIMESTAMPDEFAULTCURRENT_TIMESTAMP);-- +downDROPTABLEposts;
Run the migration:
migrate-path migrations-database"postgres://postgres:secret@localhost:5432/mydb?sslmode=disable" up
Output: Theposts
table is created, linked to theusers
table viauser_id
. Runningmigrate down
reverses it.
When to Use Migrate
Migrate is ideal for teams needing alanguage-agnostic tool or working with diverse databases. It’s slightly more complex to set up than Goose but excels inCI/CD integration and cross-database compatibility.
3. Gormigrate: GORM-Friendly Migrations
Gormigrate is a migration library built specifically forGORM, a popular ORM for Go. If your project already uses GORM for database operations, Gormigrate is a natural fit, letting you define migrations in Go code alongside your models.
Key Features
- GORM integration: Leverages GORM’s ORM capabilities for migrations.
- Programmatic migrations: Write migrations in Go, not SQL.
- Rollback support: Easily undo migrations with built-in rollback functions.
Example: Migrating a Products Table with Gormigrate
Here’s a complete example of creating aproducts
table using Gormigrate.
packagemainimport("log""gorm.io/driver/postgres""gorm.io/gorm""github.com/go-gormigrate/gormigrate/v2")typeProductstruct{IDuint`gorm:"primaryKey"`Namestring`gorm:"type:varchar(100);not null"`Pricefloat64CreatedAttime.Time}funcmain(){dsn:="host=localhost user=postgres password=secret dbname=mydb port=5432 sslmode=disable"db,err:=gorm.Open(postgres.Open(dsn),&gorm.Config{})iferr!=nil{log.Fatal(err)}m:=gormigrate.New(db,gormigrate.DefaultOptions,[]*gormigrate.Migration{{ID:"20250607101900",Migrate:func(tx*gorm.DB)error{returntx.AutoMigrate(&Product{})},Rollback:func(tx*gorm.DB)error{returntx.Migrator().DropTable("products")},},})iferr:=m.Migrate();err!=nil{log.Fatalf("Could not migrate: %v",err)}log.Println("Migration completed")}// Output: Migration completed
Output: Theproducts
table is created with columns forid
,name
,price
, andcreated_at
. Runningm.Rollback()
drops the table.
When to Use Gormigrate
Use Gormigrate if your project isheavily invested in GORM and you prefer defining migrations in Go. It’s less flexible for raw SQL lovers and not ideal for non-GORM projects.
4. SQLx with Custom Migrations: Roll Your Own
SQLx isn’t a migration tool per se, but it’s a powerful library for working with SQL in Go. You can build acustom migration system using SQLx to execute migration scripts, giving you ultimate flexibility. This approach is best for teams who wantfull control over their migration logic.
Key Features
- SQLx flexibility: Combine SQLx’s query execution with your own migration tracking.
- Customizable: Build exactly the migration workflow you need.
- No external CLI: Everything runs in your Go code.
Example: Custom Migration with SQLx
Here’s a simple migration system using SQLx to create anorders
table.
packagemainimport("log""github.com/jmoiron/sqlx"_"github.com/lib/pq")typeMigrationstruct{IDstringUpQuerystring}funcmain(){db,err:=sqlx.Connect("postgres","user=postgres password=secret dbname=mydb sslmode=disable")iferr!=nil{log.Fatal(err)}// Create migrations table if it doesn't exist_,err=db.Exec(`CREATE TABLE IF NOT EXISTS migrations (id VARCHAR(50) PRIMARY KEY)`)iferr!=nil{log.Fatal(err)}migrations:=[]Migration{{ID:"20250607102000_create_orders",UpQuery:` CREATE TABLE orders ( id SERIAL PRIMARY KEY, user_id INTEGER REFERENCES users(id), total DECIMAL(10,2), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP )`,},}for_,m:=rangemigrations{varexistsboolerr:=db.Get(&exists,"SELECT EXISTS (SELECT 1 FROM migrations WHERE id = $1)",m.ID)iferr!=nil{log.Fatal(err)}if!exists{_,err:=db.Exec(m.UpQuery)iferr!=nil{log.Fatal(err)}_,err=db.Exec("INSERT INTO migrations (id) VALUES ($1)",m.ID)iferr!=nil{log.Fatal(err)}log.Printf("Applied migration: %s",m.ID)}}}// Output: Applied migration: 20250607102000_create_orders
Output: Theorders
table is created, and the migration is tracked in amigrations
table.
When to Use SQLx
Choose SQLx forcustom migration workflows when existing tools don’t fit your needs. It requires more setup but offers unmatched flexibility.
5. Flyway (via Go Integration): Enterprise-Grade Migrations
Flyway is a Java-based migration tool that’s widely used in enterprise settings. While not Go-native, you can integrate it into Go projects using its CLI or by calling its Java library. Flyway is great for teams needingrobust versioning andaudit-ready migration history.
Key Features
- Versioned migrations: Strict versioning ensures predictable schema changes.
- Enterprise-friendly: Supports complex workflows and multiple environments.
- SQL-based: Write migrations in plain SQL.
Example: Running Flyway with Go
Here’s how you can use Flyway’s CLI in a Go project to create acategories
table.
First, download Flyway and set up amigrations
folder with a file namedV1__create_categories_table.sql
:
CREATETABLEcategories(idSERIALPRIMARYKEY,nameVARCHAR(100)NOTNULL,created_atTIMESTAMPDEFAULTCURRENT_TIMESTAMP);
Run Flyway via a Go program:
packagemainimport("log""os/exec")funcmain(){cmd:=exec.Command("flyway","-url=jdbc:postgresql://localhost:5432/mydb","-user=postgres","-password=secret","migrate")output,err:=cmd.CombinedOutput()iferr!=nil{log.Fatalf("Flyway failed: %v\n%s",err,output)}log.Println("Flyway migration completed")log.Println(string(output))}// Output: Flyway migration completed// (Flyway CLI output follows)
Output: Thecategories
table is created, and Flyway tracks the migration in itsflyway_schema_history
table.
When to Use Flyway
Flyway is ideal forenterprise projects or teams already using it in polyglot environments. It’s overkill for small projects due to its Java dependency and setup complexity.
6. Comparing the Tools: Which One Fits Your Project?
To help you choose, here’s a comparison of the tools based on key criteria.
Tool | Database Support | Migration Type | Ease of Use | Best For |
---|---|---|---|---|
Goose | PostgreSQL, MySQL, SQLite, etc. | SQL, Go | High | Small to medium projects |
Migrate | Almost all databases | SQL | Medium | CI/CD pipelines, diverse DBs |
Gormigrate | GORM-supported DBs | Go | High | GORM-based projects |
SQLx (Custom) | Any SQLx-supported DB | SQL, Go | Low | Custom workflows |
Flyway | Many (via JDBC) | SQL | Medium | Enterprise, multi-language teams |
Key takeaway: If you’re unsure, start withGoose for simplicity orMigrate for flexibility. Use Gormigrate for GORM projects, SQLx for custom needs, or Flyway for enterprise-grade requirements.
7. Tips for Smooth Migrations in Go
To wrap up, here are practical tips to make your migrations successful:
- Version your migrations: Use timestamps or sequential IDs to avoid conflicts (e.g.,
20250607102100
). - Test migrations locally: Always run migrations in a local or staging environment before production.
- Backup your database: Before applying migrations, ensure you have a backup to avoid data loss.
- Use transactions: For complex migrations, wrap changes in transactions to ensure atomicity.
- Document changes: Add comments in migration files to explain the purpose of each change.
Example: Transactional Migration with Goose
Here’s a Goose migration using a transaction for safety:
-- +goose UpBEGIN;CREATETABLEpayments(idSERIALPRIMARYKEY,user_idINTEGERREFERENCESusers(id),amountDECIMAL(10,2),created_atTIMESTAMPDEFAULTCURRENT_TIMESTAMP);INSERTINTOpayments(user_id,amount)VALUES(1,99.99);COMMIT;-- +goose DownDROPTABLEpayments;
Output: Thepayments
table is created, and a sample row is inserted atomically. If anything fails, the transaction rolls back.
What’s Next for Your Go Migrations?
Choosing the right migration tool depends on your project’s size, team, and database needs.Goose andMigrate are great for most Go developers due to their simplicity and SQL focus.Gormigrate is a no-brainer for GORM users, whileSQLx offers flexibility for custom setups.Flyway suits enterprise teams needing robust versioning.
Start by experimenting with one tool in a small project. Run the examples above, tweak them for your database, and see what fits your workflow. Whichever tool you pick, prioritizeautomation,testing, andbackup strategies to keep your migrations smooth and your app reliable.
Top comments(2)

Yes. Gomigrate for example asks for both up/down SQL queries to create a migration.
Some comments may only be visible to logged-in visitors.Sign in to view all comments.
For further actions, you may consider blocking this person and/orreporting abuse