Skip to content

Alembic for Python — Complete Guide

DodaTech Updated 2026-06-28 7 min read

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

What databases does Alembic support?

Alembic supports all databases that SQLAlchemy supports: PostgreSQL, MySQL, SQLite, Oracle, MSSQL, and more. Database-specific operations are available through SQLAlchemy dialect modules.

Can I use Alembic without SQLAlchemy?

Yes. Alembic supports raw SQL operations through op.execute(). You can manage migrations without SQLAlchemy models. Auto-generation requires SQLAlchemy models.

How does Alembic handle merge conflicts in team development?

Alembic supports branching and merging. Use alembic merge to create a merge migration that combines two branches. The merge migration has multiple parent revisions.

What is the difference between alembic upgrade head and alembic upgrade +1?

upgrade head applies all pending migrations. upgrade +1 applies the next migration only. Use +1 for step-by-step application during development.

How do I bootstrap Alembic for an existing database?

Create a migration that reflects the current schema. Use alembic stamp head to mark all existing migrations as applied without running them. This tells Alembic the current state.

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