Open-source project
zzzeek/alembic avatar
zzzeek/alembic

Alembic: versioned database migrations for SQLAlchemy projects

THIS IS NOT THE OFFICIAL REPO - PLEASE SUBMIT PRs ETC AT: http://github.com/sqlalchemy/alembic

526 stars80 forksPythonMIT

At a glance

What is it?
Alembic is the migration tool written by the author of SQLAlchemy, and it covers everything from autogenerated schema diffs to a dependency graph of migration branches and SQL-only output for DBAs who cannot run DDL directly. It suits any team whose schema changes over time, and it is part of the SQLAlchemy project with its own conventions and its own release cadence.
Who is it for?
Adopt Alembic when your project uses SQLAlchemy and the schema has to change over time, because the autogenerate workflow, the offline SQL mode and the branch-aware version graph are the three things you would otherwise have to build, and they are hard to build. Do not adopt it for a project that does not use SQLAlchemy, since the whole model of the tool is a diff between a database and a SQLAlchemy model.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 3 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What a migrations tool is doing for your database

Alembic is a database migrations tool written by the author of SQLAlchemy, and the README lists the functionality it offers.

It can emit ALTER statements to a database in order to change the structure of tables and other constructs. It provides a system whereby migration scripts may be constructed, where each script indicates a particular series of steps that can upgrade a target database to a new version, and optionally a series of steps that can downgrade similarly, doing the same steps in reverse. And it allows the scripts to execute in some sequential manner.

That last point, the downgrade path, is the one most tools skip. A migration that cannot be reversed is a one-way door, and a team that discovers it halfway through a release has no way back other than restoring a backup. Alembic builds both directions into the model of a script from the start.

The audience is any team whose schema outlives a single deploy. The project is part of the SQLAlchemy Project, and the README says it adheres to the same standards and conventions as the core project. The licence is MIT, and the documentation and status live at alembic.sqlalchemy.org.

One thing to note before you read further, because it changes what this repository is. The project description says plainly: this is not the official repo, please submit PRs etc at the upstream `sqlalchemy/alembic`. So this is a mirror or a fork that tracks the upstream project, and any change you want to make belongs upstream, not here.

Autogenerate, and the edit step that follows it

The feature that makes Alembic worth the setup is autogeneration, and the README is careful about what it can and cannot do.

The `--autogenerate` feature inspects the current status of a database using SQLAlchemy's schema inspection capabilities, compares it to the current state of the database model as specified in Python, and generates a series of candidate migrations, rendering them into a new migration script as Python directives. Table and column level changes can be detected, with constraints and indexes to follow as well.

The candour is in the next sentence: while real world migrations are far more complex than what can be automatically determined, Alembic can still eliminate the initial grunt work of generating new migration directives from an altered schema. The developer then edits the new file, adding additional directives and data migrations as needed, to produce a finished migration.

That edit step is not a wart, it is the design. Autogenerate gives you the mechanical part, the diff between what the database has and what your models say it should have, and a human supplies the intent, the data backfill and the careful handling of anything the tool could not infer. A tool that tried to do both would be wrong half the time.

The connection to SQLAlchemy is also what makes the diff possible. Because the target state of the schema is expressed as a SQLAlchemy model, the comparison is between two structured representations rather than between a database and a set of expectations typed out by hand.

Minimalist operations, and the ALTER library you can use alone

A second goal in the README is minimalist script construction. Basic operations like renaming tables and columns, adding or removing columns, and changing column attributes can be performed through one line commands like `alter_column()`, `rename_table()` and `add_constraint()`. There is no need to recreate full SQLAlchemy Table structures for simple operations like these, because the functions themselves generate minimalist schema structures behind the scenes to achieve the given DDL sequence.

That is a meaningful difference for a migration file. A migration that reads as a list of intentions is reviewable. A migration that reconstructs a whole table definition to change one column is noise, and a reviewer has to work out which parts of the reconstruction actually matter.

The third goal is broader than migrations for SQLAlchemy applications. It is to provide a library of ALTER constructs that can be used by any SQLAlchemy application. The DDL constructs build upon SQLAlchemy's own DDLElement base and can be used standalone by any application or script.

So there are two audiences inside one package. Most people will use the command line workflow of environment, revision, upgrade and downgrade. But the underlying DDL construction layer is a normal Python library, and a team with a bespoke migration runner can take the constructs and drive them directly.

The packaging supports both readings. The console script is `alembic = "alembic.config:main"`, the project is described as a database migration tool for SQLAlchemy, and the runtime dependencies are minimal: `SQLAlchemy>=2.0`, `Mako` for the templating that generates scripts, `typing-extensions>=4.12`, and `tomli` on Python versions below 3.11. The optional `tz` extra pulls in `tzdata` for platforms that need it.

Offline SQL mode for the environment where DDL is a privilege

The README describes the SQL script mode as being for people who work in corporate environments, where direct access to DDL commands on a production database is a rare privilege and DBAs want textual SQL scripts.

Alembic's usage model and commands are oriented towards being able to run a series of migrations into a textual output file as easily as it runs them directly to a database. The same migration script produces either the DDL or the execution, which is the point: the reviewed artefact is the SQL.

There is a real caveat, and the README states it: care must be taken in this mode to not invoke other operations that rely upon in-memory SELECTs of rows. Alembic tries to provide helper constructs like `bulk_insert()` to help with data-oriented operations that are compatible with script-based DDL.

That constraint has teeth. Any migration that reads rows to decide what to write, such as a data transformation that depends on current contents, cannot be expressed as offline SQL in the general case, and a team that discovers this mid-release has a problem. The workaround the project points at, `bulk_insert()`, covers the common case of moving data between tables, which is what most data migrations actually are.

The transaction story is related. The default scripts ensure that all migrations occur within a transaction, and for the databases that support this, PostgreSQL and Microsoft SQL Server, migrations can be tested with no need to manually undo changes upon failure. That is a stronger guarantee than most migration tools offer, and it is the reason the offline mode is a secondary concern rather than the primary mode.

Branchable version graphs and the SQLite batch workaround

The two features that separate Alembic from a simpler migration tool are its version graph and its treatment of SQLite.

On versioning, the README describes non-linear, dependency-graph versioning. Scripts are given UUID identifiers similarly to a DVCS, and the linkage of one script to the next is achieved via human-editable markers within the scripts themselves. The structure of a set of migration files is considered as a directed acyclic graph, meaning any migration file can be dependent on any other arbitrary set of migration files, or none at all. Through this open-ended system, migration files can be organized into branches, multiple roots and mergepoints, without restriction, and commands are provided to produce new branches, roots and merges of branches automatically.

That solves a real problem. Two features developed in parallel, each on its own branch, merged back to trunk, and a hotfix that has to be applied on top of a released version and then merged forward, is a situation that a linear version counter cannot represent and that most migration tools handle by telling you to rebase manually.

On SQLite, the README is characteristically blunt: at long last, bring SQLite and its inability to ALTER things into the fold, but in such a way that SQLite's very special workflow needs are accommodated in an explicit way that makes the most of a bad situation. The mechanism is the concept of a batch migration, where multiple changes to a table can be batched together to form a series of instructions for a single, subsequent move-and-copy workflow. The README notes you can even use the move-and-copy workflow for other databases if you want to recreate a table in the background on a busy system.

The batch mode is a real trade-off, and it is worth being explicit about. It works, and it is the only practical way to alter columns in SQLite, but it means the operation is a table rebuild, which is slower, takes a lock, and does not preserve everything the original table had.

The environment template, and how the whole thing is wired

The first goal in the README is open ended and transparent configuration and operation, and the mechanism is template based.

A new Alembic environment is generated from a set of templates which is selected among a set of options when setup first occurs. The templates then deposit a series of scripts that define fully how database connectivity is established and how migration scripts are invoked, and the migration scripts themselves are generated from a template within that series of scripts. The scripts can then be further customized to define exactly how databases will be interacted with and what structure new migration files should take.

The layered generation is what makes the tool feel transparent rather than magic. The environment is yours to edit, the template that produces each new revision is yours to edit, and the revision itself is Python you can read in a diff. Nothing is hidden in a config file format you have to learn separately.

The repository layout matches that structure. The `alembic/` directory is the package, `docs/` holds the documentation, `tests/` the test suite, `tools/` the helper scripts, and `reap_dbs.py` is the script that recycles the test databases, which is a small but telling detail about how the project tests itself. There is a `noxfile.py` for session management, a `tox.ini` for environments, a `setup.cfg` alongside the modern `pyproject.toml`, and a `setup.py` that is now a three line file calling `setup()` with no arguments, since all the metadata moved into `pyproject.toml`. The build backend is setuptools with `setuptools>=77.0.3` required, and the version is dynamic, derived from the source tree rather than written in the manifest.

There is also a `.gitreview` file and a `CHANGES` file, and the `.pre-commit-config.yaml` shows that formatting is enforced by hooks rather than by convention.

Where Alembic is the wrong tool

The first wrong-tool case is a project that does not use SQLAlchemy. The entire model of the tool is a diff between a database and a SQLAlchemy model, and the autogenerate feature depends on SQLAlchemy's schema inspection. Without SQLAlchemy there is no target to diff against, and the tool reduces to a script runner with a lot of extra opinions.

The second is a schema that never changes, or a project where a couple of hand-written SQL files would do. Alembic's machinery is worth it once you have many developers, many environments and a deployment pipeline that runs on every release. For a small internal tool with three tables, the setup and the templates are more than the problem.

The third is data migrations that need to read the data. The offline SQL mode explicitly cannot express operations that depend on in-memory SELECTs of rows. If your migrations are mostly data transformations whose shape depends on current contents, the mode that fits your work is the online one, which means the person running it needs DDL rights on the target database. That is a deployment constraint, not a preference.

The fourth is a team that expects the tool to be the source of truth for the schema. Alembic generates candidate migrations that you then edit, so the model definitions and the migration history are two artefacts that can drift. The tool reduces the drift, it does not remove it.

The alternative worth naming is writing your own migration runner, which for a small project is genuinely viable, since a migration is often just a function that runs SQL. The difference in approach is that Alembic gives you the diff, the revision identifiers, the branch graph and the transaction handling, and the cost of that is a directory of generated Python files, a template-driven environment, and a learning curve around the revision model. A hand-rolled runner gives you total control and none of those, and the trade is between buying the standard workflow and owning every detail of it.

Maintenance, the mirror arrangement, and the SQLAlchemy tie

The last push to the default branch was on 2026-10-20, and the repository is not archived. The recent releases list is empty, with the README noting that the repository has no GitHub releases, which is normal for a library distributed on PyPI rather than as release artefacts.

The package metadata records the project as `Development Status :: 5 - Production/Stable`, with classifiers for Python 3.10 through 3.15 and for both CPython and PyPy, and it requires Python 3.10 or later.

The mirror arrangement is the fact to be aware of. The project description states that this is not the official repo and directs pull requests to the upstream `sqlalchemy/alembic` project, and the `pyproject.toml` URLs point at the upstream repository for the source and the issue tracker while listing the documentation at alembic.sqlalchemy.org. The `alembic` package on PyPI is the one built from the upstream project, so for practical purposes the difference between this tree and upstream is a matter of provenance rather than function, but any patch you want to contribute has to go upstream.

The SQLAlchemy tie is deeper than shared authorship. Alembic is part of the SQLAlchemy Project and adheres to the same standards and conventions as the core project, it builds its DDL constructs on SQLAlchemy's own DDLElement base, and it requires `SQLAlchemy>=2.0` at runtime. The README also points at the SQLAlchemy Community Guide for coding and participation guidelines, and it has a code of conduct that emphasises polite, thoughtful and constructive communication.

The licence is MIT, with a `LICENSE` file in the tree and the licence recorded in the metadata. The `CHANGES` file at the top level is the changelog, and the `docs/` directory is the source of the published documentation. For a team already on SQLAlchemy, the practical decision is short: read the changelog, check that the required SQLAlchemy version matches the one your application pins, and put the migrations in the same repository as the models so that every schema change and its migration land in one review.

Editorial conclusion

Adopt Alembic when your project uses SQLAlchemy and the schema has to change over time, because the autogenerate workflow, the offline SQL mode and the branch-aware version graph are the three things you would otherwise have to build, and they are hard to build. Do not adopt it for a project that does not use SQLAlchemy, since the whole model of the tool is a diff between a database and a SQLAlchemy model. Before you start, note that the repository you are looking at is a mirror, since the project description says plainly that this is not the official repo and that pull requests belong at the upstream `sqlalchemy/alembic`, and check the `SQLAlchemy>=2.0` dependency, since the metadata pins it and your project has to agree.

Frequently asked questions

What is Alembic and what does it do?

It is a database migrations tool written by the author of SQLAlchemy. It can emit ALTER statements to change the structure of tables and other constructs, provides a system of migration scripts that can upgrade a database to a new version and optionally downgrade it, and allows the scripts to execute in a sequential manner.

How does Alembic autogenerate a migration?

The `--autogenerate` feature inspects the current state of the database using SQLAlchemy's schema inspection, compares it to the database model as specified in Python, and generates candidate migrations rendered as Python directives in a new script. The README is clear that real world migrations are more complex than what can be determined automatically, so the developer then edits the generated file.

What does Alembic do about databases where the DBA controls DDL?

It supports running a series of migrations into a textual SQL output file as easily as running them directly against a database, since direct access to DDL commands on a production database is a rare privilege in corporate environments. The caveat is that operations relying on in-memory SELECTs of rows cannot be used in this mode, and Alembic provides helper constructs such as `bulk_insert()` for data operations that are compatible with script-based DDL.

How does Alembic handle SQLite, which cannot ALTER tables?

Through the concept of a batch migration, where multiple changes to a table are batched together into a series of instructions for a single subsequent move-and-copy workflow. The README says the same move-and-copy workflow can be used for other databases if you want to recreate a table in the background on a busy system.

Is this repository the official Alembic source?

No. The project description says plainly that this is not the official repo and asks that pull requests and other contributions go to the upstream `sqlalchemy/alembic` project. The `pyproject.toml` likewise points the source and issue tracker URLs at the upstream repository, and the documentation lives at alembic.sqlalchemy.org.

Official sources

  1. Issues
  2. License: MIT
  3. Project website
  4. README
  5. zzzeek/alembic on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/zzzeek-alembic.svg)](https://hysenlabs.com/projects/zzzeek-alembic)

Was this useful?