Upgrade and Downgrade: Alembic Revision Management
After writing migrations, you need to apply them to your database. Alembic tracks which revisions have been applied using a version table and gives you fine-grained control over moving forward and backward through your migration history. Understanding upgrade, downgrade, and revision dependencies is critical for safe deployments and local development.
The Alembic Version Table
When you run your first migration, Alembic creates an alembic_version table in your database:
version_num
-----------------------------------
001a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5
002f9a8b7c6d5e4f3g2h1i0j9k8l7m6n
003x2y3z4a5b6c7d8e9f0g1h2i3j4k5l
Each row is the revision ID (a hash) of an applied migration. Alembic reads this table to know which migrations have run, which are pending, and what the current version is.
View Migration History
Check which migrations have been applied:
alembic current
Output:
003x2y3z4a5b6c7d8e9f0g1h2i3j4k5l (head)
View the full history including all available revisions:
alembic history --verbose
Output:
Rev ID: 001a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5 (001_initial_schema.py)
Parent: <base>
Path: migrations/versions/001_initial_schema.py
... docstring for the migration ...
Rev ID: 002f9a8b7c6d5e4f3g2h1i0j9k8l7m6n (002_add_phone_column.py)
Parent: 001a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5
Path: migrations/versions/002_add_phone_column.py
...
Rev ID: 003x2y3z4a5b6c7d8e9f0g1h2i3j4k5l (003_create_posts_table.py)
Parent: 002f9a8b7c6d5e4f3g2h1i0j9k8l7m6n
Path: migrations/versions/003_create_posts_table.py
...
This shows the chain of migrations: each has a parent, and each one depends on the previous one completing successfully.
Upgrade to Latest
Apply all pending migrations:
alembic upgrade head
This runs every migration from the current version to head (the latest). Output:
INFO [alembic.runtime.migration] Context impl PostgreSQLImpl
INFO [alembic.runtime.migration] Will assume transactional DDL is supported by the environment
INFO [alembic.migration] Running upgrade 002f9a8b... 003x2y3z..., add_email_column
Upgrade to a Specific Revision
If you have three migrations but want to apply only the first two:
alembic upgrade 002f9a8b7c6d5e4f3g2h1i0j9k8l7m6n
This applies migrations up to (and including) revision 002f.... Useful for testing or rolling out changes gradually in production.
You can also use a relative revision notation:
alembic upgrade +2 # Apply the next 2 pending migrations
Downgrade Strategies
Rollback is critical for handling broken deployments. Alembic supports several approaches.
Downgrade by One Revision
Revert the last applied migration:
alembic downgrade -1
This runs the downgrade() function in the most recent migration. Useful for quick local testing or rolling back the last deployed change.
Downgrade Multiple Revisions
Roll back the last 3 migrations:
alembic downgrade -3
Downgrade to a Specific Revision
Roll back to a specific point in history:
alembic downgrade 001a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5
This runs the downgrade() function in each migration from the current version down to (but not including) the specified revision. All data in schema changes made after that revision are lost, so use with care.
Downgrade to Base (Erase Everything)
Drop all tables and indexes:
alembic downgrade base
This is destructive and rarely used in production. Useful for development/testing to reset your database to a clean state.
Pre-Deployment Testing
Before deploying migrations to production, test them on a staging database:
# On staging: apply migrations
alembic upgrade head
# Verify your application works with the new schema
# Run your test suite
pytest
# Test that downgrade works (in case you need to rollback)
alembic downgrade -1
alembic upgrade head
This confirms both upgrade and downgrade work before production deployment.
Viewing Migration SQL Without Applying
See the exact SQL that will run before you execute it:
alembic upgrade head --sql
Output (example):
-- Running upgrade 002f9a8b... 003x2y3z...
ALTER TABLE users ADD COLUMN email VARCHAR(100);
-- Running upgrade 003x2y3z... 004w1v2u3t4s...
CREATE TABLE posts (
id INTEGER NOT NULL,
user_id INTEGER NOT NULL,
title VARCHAR(200) NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY(user_id) REFERENCES users (id)
);
-- ...
No SQL is actually executed; you're just seeing what will happen. Good for code review.
Handling Migration Conflicts in Teams
When multiple developers create migrations for the same feature, they may have conflicting revision IDs or dependencies. Example conflict:
Developer A creates: 003_add_admin_column.py
Developer B creates: 003_add_status_enum.py # Same revision number!
When you merge both branches, you have two 003_* files. Alembic detects this when you try to run alembic upgrade head and fails.
Resolving Conflicts
- Decide the order: A's migration should come before B's.
- Rename B's file to
004_add_status_enum.py. - Edit B's migration to reference A's revision as its parent:
# migrations/versions/004_add_status_enum.py
"""add status enum"""
from alembic import op
# Indicate that this migration depends on the previous one
revision = '004x9y8z7a6b5c4d3e2f1g0h9i8j7k6l'
down_revision = '003x8y7z6a5b4c3d2e1f0g9h8i7j6k5l' # A's revision
branch_labels = None
depends_on = None
- Test:
alembic upgrade headshould apply both migrations in order.
Migration Metadata and Branching
Every migration file has metadata at the top:
# migrations/versions/003_add_email_column.py
"""add email column"""
revision = '003x2y3z4a5b6c7d8e9f0g1h2i3j4k5l'
down_revision = '002f9a8b7c6d5e4f3g2h1i0j9k8l7m6n'
branch_labels = None
depends_on = None
revision— unique hash for this migrationdown_revision— the migration before this one (the parent)branch_labels— for managing branching (advanced; see next article)depends_on— explicit dependencies on other migrations
Do not edit these by hand; Alembic generates them automatically when you run alembic revision.
Autogenerating with Existing Revisions
When you have existing migrations and want to autogenerate a new one, run:
alembic revision --autogenerate -m "Add user status"
Alembic automatically determines the parent revision (the current head) and creates the new migration with the correct down_revision value.
Key Takeaways
alembic currentshows the currently applied revisionalembic upgrade headapplies all pending migrations in orderalembic upgrade <revision>applies up to a specific revisionalembic downgrade -1rolls back the last migration;-Nrolls back N migrations- Use
alembic upgrade head --sqlto preview SQL before applying - Test
upgradeanddowngradeon staging before production deployments - In teams, resolve conflicting revision numbers by renaming and updating the
down_revisionfield
Frequently Asked Questions
Can I skip a migration without breaking the chain?
No. Alembic enforces a linear chain: each migration depends on its parent. If you skip revision 2, revision 3 won't run because its parent is missing. If you must skip a migration, mark it as already applied with alembic stamp <revision>, then create a new migration that undoes the changes.
What if a migration fails partway through?
Alembic wraps each migration in a transaction. If any statement fails, the entire migration is rolled back and your database remains in its previous state. Your migration either fully applies or not at all (atomic semantics).
How do I migrate a database without running a migration?
Use alembic stamp <revision>. This marks a revision as applied in the alembic_version table without running any SQL. Useful if you've manually applied changes or if you're starting to use Alembic on an existing database.
Can I reorder migrations that have already been applied?
No. Never reorder applied migrations. The down_revision chain is immutable once applied. If you need to "undo" an old migration, create a new migration that reverses its changes.