atlas-migrations
Authors and runs Atlas database schema migrations - declarative HCL or SQL schema definition with `atlas schema apply` for desired-state apply OR `atlas migrate diff` to generate versioned migrations against a dev DB; `atlas migrate apply` to deploy; `atlas migrate lint` to flag destructive / locking / data-loss patterns; `atlas migrate hash` to detect tampering. Supports PostgreSQL, MySQL, SQL Server, ClickHouse, SQLite, MariaDB, Snowflake, Oracle, Redshift, Spanner, CockroachDB, Databricks. Use when the user wants Terraform-style declarative DB schema management or modern SQL-first migration linting beyond Flyway / Liquibase.
atlas-migrations
Overview
Per atlasgo.io/getting-started:
"Atlas is a language-independent tool for managing and migrating database schemas using modern DevOps principles. Define your desired schema, and Atlas will plan, lint, test, and safely apply the changes - like Terraform, but for databases."
Two operating modes:
Most production teams use versioned mode for the audit trail; dev loops often use declarative mode for fast iteration.
When to use
Step 1 - Install
Per at-start:
# curl install script
curl -sSf https://atlasgo.sh | sh
# Homebrew
brew install ariga/tap/atlas
# Direct binary download (per platform):
# atlasbinaries.com/atlas/atlas-{linux-amd64,linux-arm64,
# darwin-amd64,darwin-arm64,
# windows-amd64}-latestStep 2 - Define schema
Per at-start, schema can be SQL (most common) or HCL.
SQL example (PostgreSQL):
-- schema.sql
CREATE TABLE users (
id serial PRIMARY KEY,
name varchar(255) NOT NULL,
email varchar(255) UNIQUE NOT NULL,
created_at timestamptz DEFAULT now()
);HCL example (same schema in Atlas's HCL DSL):
table "users" {
schema = schema.public
column "id" {
null = false
type = serial
}
column "email" {
null = false
type = varchar(255)
}
primary_key { columns = [column.id] }
index "unique_email" { unique = true; columns = [column.email] }
}The dev DB referenced via --dev-url is a temporary scratch database Atlas uses to compute the diff (Atlas applies the desired state to the scratch DB, then computes the migration vs production).
Step 3 - Apply schema (declarative mode)
Per at-start:
atlas schema apply --url "$DATABASE_URL" --to file://schema.sql \
--dev-url "docker://postgres/17/dev?search_path=public"--url = target DB; --to = desired schema definition; --dev-url = scratch DB for diff computation. Atlas prints the planned changes and prompts for confirmation by default.
Step 4 - Generate versioned migrations
Per at-start:
atlas migrate diff initial --to file://schema.sql \
--dev-url "docker://postgres/17/dev?search_path=public"This generates a timestamped migration file in migrations/ (e.g., 20260506120000_initial.sql). Subsequent calls with a new schema generate new migrations against the previous state.
Step 5 - Apply versioned migrations
atlas migrate apply --url "$DATABASE_URL"Atlas tracks state in atlas_schema_revisions table - each applied migration is recorded with its hash, so tampering is detected on subsequent applies.
Step 6 - Lint migrations
Per at-start:
atlas migrate lint --dev-url "..." --latest 1Lints the most recent migration for: data loss, narrow-column operations (e.g., varchar(50) → varchar(20)), missing default on a NOT NULL add, lock-escalating ops on large tables, schema backwards-incompatibility.
The lint output is the value-add over Flyway / Liquibase - those tools are syntax-only; Atlas knows about destructive patterns.
Step 7 - Hash + integrity
Atlas computes hashes for each migration file. If a developer edits an already-applied migration, atlas migrate apply fails until atlas migrate hash re-syncs (intentional act). This protects against silent migration tampering.
Step 8 - CI integration
- uses: ariga/setup-atlas@v0
- name: Lint migrations
run: atlas migrate lint --dir "file://migrations" \
--dev-url "docker://postgres/17/dev?search_path=public" \
--latest 1
- name: Apply migrations to staging
run: atlas migrate apply --url "${{ secrets.STAGING_DB_URL }}"GitHub Action ariga/setup-atlas@v0 provides Atlas in the runner; the atlas-action orchestrator provides PR-comment integration with diff visualization.
Step 9 - Composition with sister tools
Pair with migration-blast-radius-reviewer for adversarial review beyond atlas migrate lint (e.g., reviewer applies team-specific risk policies that Atlas's built-in lint doesn't capture).
Anti-patterns
| Anti-pattern | Why it fails | Fix |
|---|---|---|
| Edit a migration after it's been applied to staging/prod | atlas migrate apply fails on hash mismatch | Add a new migration that adjusts |
Skip --dev-url | Atlas can't compute diff; commands fail or produce wrong output | Always pass --dev-url (Steps 3, 4, 6) |
Use declarative schema apply directly to production | No audit trail of applied changes | Use versioned mode in production (Step 4) |
Skip atlas migrate lint in CI | Destructive migrations slip through review | Always lint in CI (Step 8) |
atlas migrate hash after every edit (without team review) | Defeats integrity check | Hash sync only after intentional edit + team review |