What Is Alembic? Database Migrations Explained
How Alembic tracks and applies versioned schema changes for SQLAlchemy projects, and why you need it the moment a database schema has to change
Alembic manages database schema changes over time for SQLAlchemy projects — versioned migration scripts you can apply, review, and roll back, instead of hand-editing a production schema directly.
What an Alembic migration is
Each change to the schema lives in its own small Python file, called a migration or revision. The file has an upgrade() function that applies the change and a downgrade() function that undoes it, plus two identifiers: its own revision ID and the down_revision ID of the migration before it. That chain of IDs is how Alembic knows the order to apply them in, and the first file has a down_revision of None.
revision = "ae1027a6acf"
down_revision = "1975ea83b712"
from alembic import op
import sqlalchemy as sa
def upgrade():
op.add_column("account", sa.Column("last_transaction_date", sa.DateTime))
def downgrade():
op.drop_column("account", "last_transaction_date")
Alembic stores which revision a database is currently at, so it knows exactly what is left to apply.
The problem it solves
Once an application is running against a real database, changing the schema — adding a column, changing a type, adding an index — can’t just mean editing your models and hoping the database catches up. You need a record of what changed, in what order, and a way to apply that exact sequence of changes to any environment (local, staging, production) reliably. Alembic generates and manages that sequence as versioned migration scripts.
How it fits with SQLAlchemy
SQLAlchemy itself has no built-in migration system — Alembic, built by the same maintainer, is the standard tool that fills that gap. A typical workflow: change your SQLAlchemy models, run alembic revision --autogenerate to produce a draft migration script by diffing your models against the database’s current schema, review and adjust that script, then alembic upgrade head to apply it.
Getting started
pip install alembic
cd /path/to/yourproject
alembic init alembic
That creates an alembic.ini file and an alembic/ folder containing env.py, a template for new migrations and a versions/ folder for them. Then:
- Set
sqlalchemy.urlinalembic.inito your database URL. - Create a migration:
alembic revision -m "create account table". - Fill in its
upgrade()anddowngrade()functions, or let autogeneration do it (see below). - Apply it with
alembic upgrade head, whereheadmeans the most recent revision.
| Command | What it does |
|---|---|
alembic revision -m "message" | Create a new, empty migration |
alembic revision --autogenerate -m "message" | Create a migration by comparing your models to the database |
alembic upgrade head | Apply every migration not yet applied |
alembic downgrade -1 | Undo the most recent migration |
alembic current | Show which revision the database is at |
alembic history | List the migrations in order |
To make --autogenerate work, open env.py and set target_metadata to your models’ MetaData object, for example target_metadata = Base.metadata. The generated file is a draft that you review before applying.
Autogeneration has limits
Autogeneration catches most common changes — new tables, new columns, type changes — by comparing models to the live schema. It doesn’t reliably catch everything: a renamed column looks like “drop one column, add another” rather than a rename, and some changes need to be written or corrected by hand. Reviewing every autogenerated script before applying it is standard practice, not an edge case to worry about occasionally.
What about Django?
If you’re using Django instead of SQLAlchemy, you don’t need Alembic — Django ships its own built-in migration system (makemigrations/migrate) that does the equivalent job for Django’s ORM. Alembic is specifically the answer for the SQLAlchemy ecosystem, covered alongside Django’s own migrations in SQLAlchemy vs Django ORM.
When you actually need it
The moment a SQLAlchemy-backed application has a real database with real data in it, not just a fresh schema you can recreate freely — that’s when hand-editing the schema becomes risky and Alembic’s versioned, reviewable migration history becomes worth the setup.
Frequently Asked Questions
What is Alembic used for?
Managing database schema changes over time for SQLAlchemy projects — generating versioned migration scripts, applying them in order, and rolling them back if needed, instead of hand-editing a production database’s schema directly.
Is Alembic part of SQLAlchemy?
No — it’s a separate but tightly integrated tool built by the same maintainer specifically to handle migrations, which SQLAlchemy itself doesn’t include. It’s the standard pairing for any production SQLAlchemy project.
Does Alembic generate migrations automatically?
It can autogenerate a migration script by comparing your current SQLAlchemy models against the database’s actual schema, though the generated script should always be reviewed — autogeneration catches most common changes but not everything (like renamed columns, which look like a drop-and-add unless you edit the script).
What is an Alembic migration?
A migration is a small Python file that describes one change to the database schema, with an upgrade() function that applies it and a downgrade() function that reverses it. Each file records its own revision ID and the ID of the one before it, so Alembic knows the order to apply them in.
How do I get started with Alembic?
Install it with pip, run alembic init alembic in your project to create the migration environment, set your database URL in alembic.ini, create a revision with alembic revision -m “message”, then apply it with alembic upgrade head. To autogenerate migrations from your models, point target_metadata in env.py at your SQLAlchemy MetaData.
Do you need Alembic if you’re using Django instead of SQLAlchemy?
No — Django ships its own built-in migration system (makemigrations/migrate) that serves the same purpose for Django’s ORM. Alembic is specifically the SQLAlchemy-ecosystem answer to the same problem.