golang-migrate Tutorial: Implementing Database Migration in Go

golang-migrate is a database migration tool for Go applications, providing both a command-line interface and a library for managing database schema changes.

How to Use Golang Migrate to Implement Database Migration?

Golang Migrate is a powerful and widely used database migration tool for Go applications. It provides a simple CLI and Go library to handle schema changes in a consistent and version-controlled way. As of April 2025, Golang Migrate (v4.18.2) is still a top choice in the Go community for managing database structure updates across development, staging, and production environments.

How to Install Golang Migrate?

You can install golang-migrate using pre-built binaries or platform-specific package managers, depending on your operating system and preferences.

Option 1: Pre-built Binaries

For any platform (Windows, MacOS, Linux):

curl -L <https://github.com/golang-migrate/migrate/releases/download/$version/migrate.$os-$arch.tar.gz> | tar xvz

Option 2: Platform Package Managers

  • For MacOS:
brew install golang-migrate

  • For Windows (using Scoop):
scoop install migrate

  • For Linux (Debian/Ubuntu):
curl -L <https://packagecloud.io/golang-migrate/migrate/gpgkey> | apt-key add -
    echo "deb <https://packagecloud.io/golang-migrate/migrate/ubuntu/> $(lsb_release -sc) main" &gt; /etc/apt/sources.list.d/migrate.list
    apt-get update
    apt-get install -y migrate

These commands will install the latest stable version, which as of April 2025 is v4.18.2.

Basic CLI Usage

Create Migration Files

To create a new migration:

migrate create -ext sql -dir database/migration/ -seq create_users_table

This command generates two files:

  • 000001_create_users_table.up.sql - For applying the migration
  • 000001_create_users_table.down.sql - For reverting the migration

Run Migrations

To apply all pending migrations:

migrate -source file://path/to/migrations -database postgres://localhost:5432/database up

To apply a specific number of migrations:

migrate -source file://path/to/migrations -database postgres://localhost:5432/database up 2

To roll back migrations

migrate -source file://path/to/migrations -database postgres://localhost:5432/database down

To roll back a specific number of migrations:

migrate -source file://path/to/migrations -database postgres://localhost:5432/database down 2

For GitHub-hosted migrations:

migrate -source github://username:token@repository/path -database postgres://localhost:5432/database up

Using Golang Migrate in Go Projects

Golang Migrate can be imported directly into your Go application, allowing programmatic control over migrations.

Import and Use Migrate

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:///path/to/migrations",
        "postgres://localhost:5432/database?sslmode=enable")
    if err != nil {
        // Handle error
    }

    // Apply all migrations
    if err := m.Up(); err != nil {
        // Handle error
    }

    // Or apply a specific number of migrations
    if err := m.Steps(2); err != nil {
        // Handle error
    }
}

Use Existing Database Connection

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

func main() {
    db, err := sql.Open("postgres", "postgres://localhost:5432/database?sslmode=enable")
    if err != nil {
        // Handle error
    }

    driver, err := postgres.WithInstance(db, &amp;postgres.Config{})
    if err != nil {
        // Handle error
    }

    m, err := migrate.NewWithDatabaseInstance(
        "file:///migrations",
        "postgres",
        driver)
    if err != nil {
        // Handle error
    }

    // Run migrations
    if err := m.Up(); err != nil {
        // Handle error
    }
}

Best Practices for Effective Migrations

Use Consistent Naming

Follow consistent naming patterns for migration files:

  • Use sequential numbering (001, 002, etc.)
  • Include descriptive names (create_users_table, add_email_field)
  • Use lowercase and underscores for clarity

One Change per Migration

Each migration should perform a single logical change:

  • Smaller migrations are easier to review and debug
  • Focused changes reduce the risk of conflicts
  • Simple migrations are easier to roll back if needed

Make Migrations Idempotent When Possible

Idempotent migrations can be safely applied multiple times without changing the result:

-- Example of idempotent migration
CREATE TABLE IF NOT EXISTS users (
    id SERIAL PRIMARY KEY,
    username VARCHAR(100) NOT NULL
);

-- Add column if it doesn't exist
DO $$
BEGIN
    IF NOT EXISTS (
        SELECT FROM information_schema.columns
        WHERE table_name='users' AND column_name='email'
    ) THEN
        ALTER TABLE users ADD COLUMN email VARCHAR(255);
    END IF;
END
$$;

Use Transactions for Safety

Wrap migrations in transactions to ensure atomicity:

BEGIN;

-- Migration steps here

COMMIT;

This prevents partial migrations that could leave your database in an inconsistent state.

Never Edit Existing Migrations

Once a migration has been applied to any environment:

  • Consider it immutable
  • Create new migrations for additional changes
  • This prevents inconsistencies between environments
  • Maintains a clear history of database evolution

Version Control Your Migrations

Store migrations in version control alongside application code:

  • Ensures changes are tracked
  • Enables code review of database changes
  • Preserves the history of schema evolution

Recent Updates

As of April 2025, the most recent stable release is v4.18.2, which includes several improvements:

  • Bug fixes for MySQL when using sql_safe_update
  • Improved error messages for invalid sources and databases
  • Updated support for latest PostgreSQL 16, MySQL 8.4 and 9.0
  • Support for newer Ubuntu releases (including Noble Numbat 24.04)
  • Various security updates and dependency upgrades

Integrating with Atlas for Schema Planning

For developers seeking more advanced migration planning, Atlas integration offers automatic migration planning capabilities:

  1. Atlas can calculate differences between current and desired database states
  2. It generates migration plans based on those differences
  3. This allows for declarative database schema management
  4. Integrates seamlessly with existing golang-migrate workflows

To set up Atlas with golang-migrate, create a project configuration file named atlas.hcl:

env "local" {
  src = "file://schema.sql"
  dev = "docker://mysql/8/dev"

  migration {
    dir = "file://migrations"
    format = golang-migrate
  }
}

>> Read more: 

Conclusion

Golang Migrate offers a powerful, flexible approach to database schema management, bridging the gap between application development and database evolution. Its clean design, extensive feature set, and strong community support make it an excellent choice for Go developers looking to implement robust, version-controlled database migrations.

Whether used through the CLI for straightforward operations or integrated into code for programmatic control, golang-migrate provides the tools necessary for maintaining consistent, reliable database states across all environments of your application lifecycle.

>>> Follow and Contact Relia Software for more information!

  • golang
  • coding
  • Web application Development