Cheatsheet for the Alembic CLI.
Init
alembic init migrations # sync template
alembic init -t async migrations # async template
Revision (create migration)
alembic revision -m "msg" # empty migration
alembic revision --autogenerate -m "msg" # generate from model diff
alembic revision --autogenerate --rev-id abc123 -m "msg" # custom rev ID
Upgrade / downgrade
alembic upgrade head # latest
alembic upgrade +1 # one step forward
alembic upgrade <rev> # specific revision
alembic downgrade -1 # one step back
alembic downgrade <rev> # to specific
alembic downgrade base # all the way down
State queries
alembic current # current revision
alembic current --verbose # with details
alembic heads # list head revisions (>1 = branched)
alembic heads --verbose
alembic history # full history
alembic history --verbose
alembic history -r <from>:<to>
Show specific revision
alembic show <rev>
alembic show head
alembic show base
Stamp (mark as applied without running)
alembic stamp head # mark current = head
alembic stamp <rev> # mark current = specific
alembic stamp base # mark current = base (empty)
Use cases:
- Bootstrapping a fresh DB created via
Base.metadata.create_all(). - Recovering after manual schema fixes.
Branches
alembic merge <rev1> <rev2> -m "merge X+Y"
alembic branches # show branch points
Offline mode
alembic upgrade head --sql > migration.sql # generate SQL without applying
For manual review or running SQL elsewhere.
Dry run
alembic upgrade head --sql > /dev/null # validates without applying
With env vars
DATABASE_URL=postgresql://... alembic upgrade head
If env.py reads from env / Settings.
With config override
alembic -x url=postgresql://... upgrade head
Useful in CI for multi-tenant migrations.
Pythonic API
from alembic.config import Config
from alembic import command
cfg = Config("alembic.ini")
cfg.set_main_option("sqlalchemy.url", DATABASE_URL)
command.upgrade(cfg, "head")
command.revision(cfg, autogenerate=True, message="msg")
command.downgrade(cfg, "-1")
command.stamp(cfg, "head")
command.current(cfg)
command.history(cfg)
For running migrations programmatically (e.g., in tests).
In tests
import pytest
from alembic.config import Config
from alembic import command
@pytest.fixture
def migrated_db(postgres):
cfg = Config("alembic.ini")
cfg.set_main_option("sqlalchemy.url", postgres.get_connection_url())
command.upgrade(cfg, "head")
return postgres
In K8s Job (pre-deploy)
apiVersion: batch/v1
kind: Job
metadata:
name: migrate-{{ .Values.image.tag }}
annotations:
"helm.sh/hook": pre-install,pre-upgrade
spec:
template:
spec:
containers:
- name: migrate
image: myapp:{{ .Values.image.tag }}
command: ["alembic", "upgrade", "head"]
envFrom:
- secretRef: { name: app-secrets }
restartPolicy: OnFailure
backoffLimit: 3
CI verification
alembic upgrade head # apply all migrations against test DB
# Check single head
[ "$(alembic heads | wc -l)" -eq 1 ] || exit 1
# Run tests
pytest
Common mistakes
alembic upgrade headagainst prod without staging test.- Multiple heads after merge — pick one via
alembic merge. alembic stampwithout understanding consequences — DB may be at wrong state.- Running migrations from app startup with N replicas — race.
Read this next
If you want my migration command runbook, it’s at rajpoot.dev .
Building something AI-, backend-, or data-heavy and want a second pair of eyes? I do consulting and freelance work — see my projects and ways to reach me at rajpoot.dev .