Alembic for Python — Complete Guide
In this tutorial, you will learn about Alembic for Python. We cover key concepts, practical examples, and best practices to help you master this topic.
Learn Alembic for Python database migrations: install and configure Alembic, auto-generate migrations from SQLAlchemy models, write custom migrations, and integrate Alembic into deployment pipelines.
What You Learn
You will learn how to use Alembic for database migrations in Python applications: install and initialize Alembic, configure it for your database, auto-generate migrations from SQLAlchemy model changes, write custom Migration operations, and integrate Alembic into CI/CD pipelines.
Why It Matters
Alembic is the most popular migration tool for Python. It integrates with SQLAlchemy, supports auto-generation of migrations from model changes, and works with any database SQLAlchemy supports. Understanding Alembic is essential for Python web developers.
Real-World Use
DodaTech's Django application uses Alembic alongside SQLAlchemy for analytics databases. Alembic auto-generates 90% of migrations from model changes. Custom migrations handle complex data transformations. The deployment pipeline runs alembic upgrade head before application start.
Installation and Setup
# Install Alembic
pip install alembic
# Initialize Alembic in your project
alembic init alembic
# Directory structure created:
# alembic/
# env.py # Environment configuration
# script.py.mako # Migration template
# versions/ # Migration files directory
# alembic.ini # Alembic configuration
Expected output: alembic init creates the migration environment. The alembic directory contains configuration and migration files. The versions directory stores individual migration files.
Configuration
# alembic/env.py - Configure Alembic for your application
from logging.config import fileConfig
from sqlalchemy import engine_from_config, pool
from alembic import context
from myapp.models import Base # Import your SQLAlchemy models
config = context.config
# Set the database URL from environment
config.set_main_option(
'sqlalchemy.url',
os.environ.get('DATABASE_URL', 'sqlite:///app.db')
)
# Set target metadata for auto-generation
target_metadata = Base.metadata
def run_migrations_online():
connectable = engine_from_config(
config.get_section(config.config_ini_section),
prefix='sqlalchemy.',
poolclass=pool.NullPool,
)
with connectable.connect() as connection:
context.configure(
connection=connection,
target_metadata=target_metadata,
)
with context.begin_transaction():
context.run_migrations()
# alembic.ini
[alembic]
script_location = alembic
sqlalchemy.url = sqlite:///app.db
[loggers]
keys = root,sqlalchemy,alembic
[handlers]
keys = console
[formatters]
keys = generic
Expected output: env.py configures Alembic to use your application's database URL and SQLAlchemy models. The metadata object tells Alembic what the schema should look like for auto-generation.
Auto-Generating Migrations
# After changing your SQLAlchemy models, auto-generate a migration
alembic revision --autogenerate -m "add phone to users"
# Output:
# Generating /alembic/versions/abc123def456_add_phone_to_users.py
# Detected added column: users.phone
# Review the generated migration
# Apply it
alembic upgrade head
# Example model change
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String(100))
email = Column(String(100))
phone = Column(String(20)) # New column
# Auto-generated migration
"""add phone to users
Revision ID: abc123def456
Revises: prev_revision_id
Create Date: 2026-06-28 12:00:00.000000
"""
revision = 'abc123def456'
down_revision = 'prev_revision_id'
def upgrade():
op.add_column('users', sa.Column('phone', sa.String(20), nullable=True))
def downgrade():
op.drop_column('users', 'phone')
Expected output: alembic revision --autogenerate compares the current database schema to the model metadata and generates a migration for the differences. Review auto-generated migrations before applying.
Custom Migration Operations
"""merge two user name columns
Revision ID: def789abc123
Revises: abc123def456
"""
from alembic import op
import sqlalchemy as sa
revision = 'def789abc123'
down_revision = 'abc123def456'
def upgrade():
# Add combined column
op.add_column('users',
sa.Column('full_name', sa.String(200), nullable=True)
)
# Backfill data
op.execute("""
UPDATE users
SET full_name = first_name || ' ' || last_name
WHERE full_name IS NULL
""")
# Make NOT NULL after backfill
op.alter_column('users', 'full_name', nullable=False)
# Drop old columns
op.drop_column('users', 'first_name')
op.drop_column('users', 'last_name')
def downgrade():
# Recreate old columns
op.add_column('users',
sa.Column('first_name', sa.String(100), nullable=True)
)
op.add_column('users',
sa.Column('last_name', sa.String(100), nullable=True)
)
# Restore data
op.execute("""
UPDATE users
SET first_name = SPLIT_PART(full_name, ' ', 1),
last_name = SPLIT_PART(full_name, ' ', 2)
""")
# Drop combined column
op.drop_column('users', 'full_name')
Expected output: Custom migrations handle operations beyond auto-generation: data transformations, complex schema changes, and reversible data migrations. Always test custom migrations thoroughly.
Managing Migrations
# List current version
alembic current
# View migration history
alembic history
# Apply all pending migrations
alembic upgrade head
# Apply next migration
alembic upgrade +1
# Rollback one migration
alembic downgrade -1
# Rollback to specific revision
alembic downgrade abc123def456
# View SQL without executing
alembic upgrade head --sql
# Mark migration as applied without running
alembic stamp abc123def456
Expected output: Alembic commands manage migration state. upgrade head applies all pending migrations. downgrade -1 reverts the last migration. --sql shows the SQL without executing. stamp marks migrations as applied for bootstrapping.
CI/CD Integration
# Dockerfile - Run migrations at container start
CMD ["sh", "-c", "alembic upgrade head && uvicorn app.main:app"]
# docker-compose.yml
services:
app:
image: myapp:latest
command: >
sh -c "alembic upgrade head && uvicorn app.main:app"
depends_on:
db:
condition: service_healthy
db:
image: postgres:16
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
# CI/CD pipeline (GitLab CI)
migrations:
stage: migrate
script:
- alembic upgrade head
environment: production
only:
- main
Expected output: Alembic migrations run automatically on deployment. The container runs alembic upgrade head before starting the application server. CI/CD pipelines have a dedicated migration stage.
Common Mistakes
1. Auto-Generated Migrations Without Review
Auto-generated migrations may miss changes, include unintended changes, or generate incorrect SQL. Always review the generated migration file. Test it against a development database.
2. Modifying Models Without Generating Migration
Changing models without generating a migration causes mismatch between the code and database. Run alembic revision --autogenerate after model changes. Commit the generated migration with the code.
3. Running Migrations Outside Transaction
Some databases (MySQL) do not support DDL within transactions. If a migration fails mid-way, the schema is partially applied. Test on a copy. Have a rollback plan.
4. Not Using Environment Variables for Database URL
Hardcoded database URLs in alembic.ini are committed to git. Use environment variables. Configure env.py to read DATABASE_URL at runtime.
5. Ignoring Migration Dependencies
Alembic tracks dependencies through down_revision chains. Breaking the chain (merging incorrectly) causes ordering issues. Use alembic merge for parallel development branches.
Practice Questions
1. How do you auto-generate a migration in Alembic?
Run alembic revision --autogenerate -m "description". Alembic compares the current database schema to SQLAlchemy model metadata and generates a migration file with detected changes.
2. What is the purpose of target_metadata in env.py?
target_metadata is the SQLAlchemy Base.metadata. Alembic uses it to detect schema changes during auto-generation. Without it, --autogenerate cannot detect model changes.
3. How do you rollback a migration in Alembic?
Run alembic downgrade -1 to rollback one migration, or alembic downgrade revision_id to rollback to a specific version. The down migration must be properly implemented.
4. How do you integrate Alembic into a Docker deployment?
Run alembic upgrade head in the container's startup command before the application server. This ensures the schema is up-to-date before the application starts accepting requests.
Challenge
Set up Alembic for a Python project with: SQLAlchemy models (User, Order, Product), auto-generated migrations for model changes, custom migration for data transformation, configuration reading DATABASE_URL from environment, and Docker container that runs migrations on startup.
FAQ
Mini Project: Alembic Migration Setup
Set up Alembic for a Flask/FastAPI application: initialize Alembic, configure env.py with SQLAlchemy models, create models (User, Post, Comment), auto-generate initial migration, add custom migration for a data transformation, create a merge migration for parallel branches, and set up Docker integration for automatic migration on startup.
What's Next
Now that you know Alembic, explore Flyway for Java for migrations in Java applications.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro