This cheatsheet provides a concise guide to using the golang-migrate/migrate
library for database schema migrations. It covers installation, file structure, migration commands (up, down, force down), and best practices.
1. Overview
-
What is golang-migrate/migrate?
- A CLI tool and Go library for managing database schema migrations.
- Supports incremental, version-controlled, and reversible changes to database schemas.
- Compatible with databases like PostgreSQL, MySQL, SQLite, and more.
- Migrations are written as SQL files (or Go code for advanced cases).
-
Key Concepts
- Up Migration: Applies changes to evolve the schema (e.g., create tables, add columns).
- Down Migration: Reverts changes to roll back the schema (e.g., drop tables, remove columns).
- Force Down: Resolves issues with a "dirty" database by forcing a specific version.
2. Installation
Install the migrate
CLI tool for your operating system.
Prerequisites
- Go installed (for Go-based CLI compilation, optional).
- A supported database (e.g., PostgreSQL, MySQL) with connection details.
Install via Package Managers
- MacOS (Homebrew):
brew install golang-migrate
- Linux (via curl):
curl -L https://github.com/golang-migrate/migrate/releases/download/v4.17.1/migrate.linux-amd64.tar.gz | tar xvz
sudo mv migrate /usr/local/bin/
- Windows (Scoop):
scoop install migrate
- Verify Installation:
migrate -version
Install via Go (Optional)
If you prefer to build from source or use as a library:
go get -u github.com/golang-migrate/migrate/v4
3. File Structure
Organize migration files in a dedicated directory for version control and clarity.
Recommended Structure
project/
├── database/
│ ├── migrations/
│ │ ├── 202507120001_create_users_table.up.sql
│ │ ├── 202507120001_create_users_table.down.sql
│ │ ├── 202507120002_add_email_index.up.sql
│ │ ├── 202507120002_add_email_index.down.sql
│ └── schema.sql (optional: schema dump)
├── main.go
└── Makefile (optional: for migration shortcuts)
-
Naming Convention: Files are named
<timestamp>_<description>.<up/down>.sql
.- Example:
202507120001_create_users_table.up.sql
for creating a table. - Example:
202507120001_create_users_table.down.sql
for dropping it.
- Example:
-
Directory Path: Typically
database/migrations/
(relative to project root).
Example Migration Files
- 202507120001_create_users_table.up.sql:
-- Create users table
CREATE TABLE users (
id SERIAL PRIMARY KEY,
username VARCHAR(50) NOT NULL,
email VARCHAR(100) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
- 202507120001_create_users_table.down.sql:
-- Drop users table
DROP TABLE IF EXISTS users;
- 202507120002_add_email_index.up.sql:
-- Add index on email column
CREATE INDEX idx_email ON users(email);
- 202507120002_add_email_index.down.sql:
-- Drop index on email column
DROP INDEX IF EXISTS idx_email;
4. Usage
Run migrations using the migrate
CLI with a database connection string and migration path.
Basic Commands
- Create a New Migration:
migrate create -ext sql -dir database/migrations -seq create_table_name
-
-ext sql
: Specifies SQL file extension. -
-dir
: Path to migration directory. -
-seq
: Generates a sequential timestamp. -
Example output: Creates
database/migrations/202507120003_create_table_name.up.sql
and.down.sql
.- Apply Migrations (Up):
migrate -path database/migrations/ -database "postgresql://username:password@localhost:5432/dbname?sslmode=disable" -verbose up
- Applies all pending migrations in order.
-
-path
: Directory containing migration files. -
-database
: Database connection string (replace with your DB details). -
-verbose
: Shows detailed output.- Revert Migrations (Down):
migrate -path database/migrations/ -database "postgresql://username:password@localhost:5432/dbname?sslmode=disable" -verbose down
- Reverts the last applied migration.
-
Use
down N
to revert the lastN
migrations (e.g.,down 2
).- Force Down (Fix Dirty Database):
migrate -path database/migrations/ -database "postgresql://username:password@localhost:5432/dbname?sslmode=disable" force VERSION
- Use when a migration fails and marks the database as "dirty," preventing further migrations.
- Replace
VERSION
with the desired migration version (e.g.,202507120001
). - Caution: Ensure the database state matches the forced version to avoid inconsistencies.
-
Example: If migration
202507120002
fails, fix the error in the.up.sql
file, then run:
migrate -path database/migrations/ -database "postgresql://username:password@localhost:5432/dbname?sslmode=disable" force 202507120001
This sets the database to version
202507120001
, clearing the dirty state.- Check Migration Status:
migrate -path database/migrations/ -database "postgresql://username:password@localhost:5432/dbname?sslmode=disable" version
- Displays the current schema version.
Using with a Makefile (Optional)
Simplify commands with a Makefile
:
# Makefile
DB_URL=postgresql://username:password@localhost:5432/dbname?sslmode=disable
migration_up:
migrate -path database/migrations/ -database "$(DB_URL)" -verbose up
migration_down:
migrate -path database/migrations/ -database "$(DB_URL)" -verbose down
migration_force:
migrate -path database/migrations/ -database "$(DB_URL)" force $(VERSION)
- Run:
make migration_up
,make migration_down
, ormake migration_force VERSION=202507120001
.
5. Best Practices
- Version Control: Commit migration files to your repository (e.g., Git) for team collaboration.
- Test Migrations: Test migrations in a staging environment before applying to production.
-
Reversible Migrations: Always define
down
migrations to allow rollbacks. - Avoid Destructive Changes: Use non-destructive changes (e.g., add nullable columns) to minimize data loss risks.
- Backup Database: Always back up your database before running migrations.
-
Handle Dirty Databases:
- If a migration fails, check the error, fix the
.up.sql
or.down.sql
file, and useforce VERSION
cautiously. - Verify the database state matches the forced version.
- If a migration fails, check the error, fix the
6. Common Issues and Solutions
-
Error: Dirty database version X. Fix and force version:
- Cause: A migration failed, leaving the database in a "dirty" state.
- Solution: Fix the problematic migration file, then run
migrate force X
to set the correct version.
- Migration order issues: Ensure migration files are applied sequentially based on timestamps.
- Connection errors: Verify the database connection string and ensure the database is running.
7. Example Workflow
- Create a migration:
migrate create -ext sql -dir database/migrations -seq add_posts_table
- Edit
database/migrations/202507120003_add_posts_table.up.sql
:
-- Create posts table
CREATE TABLE posts (
id SERIAL PRIMARY KEY,
user_id INT REFERENCES users(id),
title VARCHAR(255) NOT NULL
);
- Edit
database/migrations/202507120003_add_posts_table.down.sql
:
-- Drop posts table
DROP TABLE IF EXISTS posts;
- Apply the migration:
migrate -path database/migrations/ -database "postgresql://username:password@localhost:5432/dbname?sslmode=disable" up
- Revert if needed:
migrate -path database/migrations/ -database "postgresql://username:password@localhost:5432/dbname?sslmode=disable" down
-
If migration fails and database is dirty:
- Fix the error in the migration file.
- Force to the last successful version:
migrate -path database/migrations/ -database "postgresql://username:password@localhost:5432/dbname?sslmode=disable" force 202507120002
8. Additional Resources
- Official GitHub: golang-migrate/migrate
- Supported Databases: PostgreSQL, MySQL, MariaDB, SQLite, SQL Server, and more.
- CLI Documentation: Run
migrate -help
for full command details.
Top comments (0)