Database Migrations in Go (Golang): A Complete Guide to Tools

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Database Migrations in Go (Golang): How to Manage Data Schema



When developing applications in Go, sooner or later you need to change the database structure: add new tables, modify existing columns, create indexes. Manually executing SQL scripts across different environments (local development, staging, production) leads to errors and desynchronization. The solution is database migrations.



The Go ecosystem has several mature libraries for managing migrations. In this article, we will thoroughly examine three of the most popular tools: golang-migrate/migrate, pressly/goose, and rubenv/sql-migrate. You will learn how to install, configure, and use each one, as well as get acquainted with best practices.



What are migrations and why are they needed?



A migration is a file (or a set of files) that contains SQL commands for changing the database schema. Each migration has a unique identifier (usually a timestamp or number) and two directions: up (applying changes) and down (reverting changes).



Main advantages of using migrations:

  • Schema versioning — the change history is stored in the repository alongside the code.
  • Reproducibility — identical changes across all environments.
  • Rollback — the ability to quickly revert to a previous schema version.
  • Team collaboration — developers do not overwrite each other's changes.


1. golang-migrate/migrate



The most popular library for migrations in Go. Supports PostgreSQL, MySQL, SQLite, MongoDB, and many other databases.



Installation

go get -u github.com/golang-migrate/migrate/v4go install -tags 'postgres' github.com/golang-migrate/migrate/v4/cmd/migrate@latest


Migration Structure

Create a migrations folder and add files with a prefix in the format {timestamp}_{title}.{direction}.sql:

migrations/  000001_create_users_table.up.sql  000001_create_users_table.down.sql  000002_add_email_column.up.sql  000002_add_email_column.down.sql


Migration Example (up)

-- 000001_create_users_table.up.sqlCREATE TABLE users (    id SERIAL PRIMARY KEY,    name VARCHAR(100) NOT NULL,    created_at TIMESTAMP DEFAULT NOW());


Migration Example (down)

-- 000001_create_users_table.down.sqlDROP TABLE IF EXISTS users;


Applying Migrations from Go Code

package main

import ( "github.com/golang-migrate/migrate/v4" _ "github.com/golang-migrate/migrate/v4/database/postgres" _ "github.com/golang-migrate/migrate/v4/source/file")

func main() { m, err := migrate.New( "file://migrations", "postgres://localhost:5432/mydb?sslmode=disable") if err != nil { panic(err) } if err := m.Up(); err != nil && err != migrate.ErrNoChange { panic(err) }}


2. pressly/goose



Goose is a lightweight and fast tool that also supports writing migrations in Go (not just SQL). It is ideal for projects that require code-side logic.



Installation

go get github.com/pressly/goose/v3/cmd/goose


Creating a Migration

goose create create_users_table sql


This will create a file like 20250101120000_create_users_table.sql. Contents:

-- +goose UpCREATE TABLE users (    id SERIAL PRIMARY KEY,    name VARCHAR(100) NOT NULL);

-- +goose DownDROP TABLE IF EXISTS users;


Go Migrations (Advanced Option)

Goose allows you to write migrat

Recommendations