Plain SQL migrations for Python. See the exact SQL before it runs.
Get started · Documentation · Coming from Flyway · Adopt an existing database
DBLift applies versioned .sql files to your database and remembers what ran. Same file convention as Flyway (V1__create_users.sql), installed with pip, usable as a CLI or as a Python library. Preview, undo, checksums and locking are part of the open-source package. Apache 2.0.
Try it in 60 seconds
No database server needed — this runs against a local SQLite file.
pip install "dblift[cli]"
mkdir migrations
echo "CREATE TABLE users (id INTEGER PRIMARY KEY, email TEXT NOT NULL UNIQUE);" > migrations/V1__create_users.sql
echo "DROP TABLE users;" > migrations/U1__create_users.sql
export DBLIFT_DB_URL="sqlite:///app.db"
dblift migrate --dry-run --show-sql # prints the SQL, applies nothing
dblift migrate # applies V1
dblift info # what ran, when, by whom, and whether it can be undone
dblift undo # runs U1, rolls the last migration back
For a real database, install the driver extra next to cli and point the URL at it:
pip install "dblift[cli,postgresql]"
export DBLIFT_DB_URL="postgresql+psycopg://user@localhost:5432/mydb"
export DBLIFT_DB_PASSWORD="..."
Your first migration → · Configuration →
Is this for you?
You run Flyway from a Python project. A JVM, a Docker image or a downloaded tarball, kept patched only to apply SQL files. DBLift reads the same V / R / U file names, and dblift import-flyway copies your flyway_schema_history so every environment keeps its history. What changes is the install line. Move from Flyway →
You apply SQL with a migrate.sh or a home-made migrate.py. It works until two files share a number, someone edits an applied file, a replica starts at the same time, or production turns out to be two migrations behind staging. That script is a migration tool you maintain by accident. Rename the files, run dblift baseline once per existing database, and delete it. Adopt an existing database → · Naming conventions →
You want migrations you can read in a pull request. Not revisions generated from an ORM — the SQL you wrote, reviewed as SQL, run as written. Works next to SQLAlchemy, Django, FastAPI and Flask, or with none of them.
What you get
Everything below ships in the open-source package.
| Preview | migrate --dry-run --show-sql prints every statement that would run. Same for undo. |
| Undo | Pair V3__x.sql with U3__x.sql; dblift undo rolls back one step or to a target version. Undo model → |
| Checksums | dblift validate fails when an applied file was edited, a recorded file is missing, or two files claim the same version. |
| SQL checks | dblift validate-sql reads your migration SQL without connecting and gives each script a verdict, SAFE, REVIEW or UNSAFE, for statements that destroy data, break the deployed application or lock a table. SQL checks → |
| Baseline | Adopt a database that already exists: declare "this one is at version N" and carry on from there. |
| Repeatable migrations | R__views.sql re-runs when its content changes — a home for views, functions, grants and seed data. |
| Locking | A lock table serialises concurrent runs; a second runner waits, then skips what the first one applied. |
| Python migrations | V4__backfill.py when a change needs logic. Python migrations → |
| A Python API | Sync and async clients, events and callbacks — run migrations from your app or your tests, not only from a shell. |
| Transactions | A migration that fails part-way is rolled back where the engine supports transactional DDL. |
| No lock-in | Your migrations stay plain .sql files in your repository. |
Ordering: DBLift applies out-of-order migrations by default — if
V3is applied and a teammate'sV2merges later,V2still runs. This is the opposite of Flyway's default. Use--strictorstrict_mode: trueto enforce version order. Versioning →
Use it from Python
Run migrations when your app starts:
from sqlalchemy import create_engine
from dblift.api import DBLiftClient
engine = create_engine("postgresql+psycopg://user:pass@localhost/app")
with DBLiftClient.from_sqlalchemy(engine, migrations_dir="migrations") as client:
client.migrate()
AsyncDBLiftClient (from dblift.api.async_client import AsyncDBLiftClient) does the same without blocking an event loop. API → · Async client →
Give your tests a migrated database:
pip install pytest-dblift
def test_schema_is_current(dblift_migrated_db, dblift_client):
assert dblift_client.info().pending_count == 0
Integrations: Django management commands and system check · FastAPI · Flask · SQLAlchemy · OpenTelemetry spans · CI/CD recipes for GitHub Actions, GitLab CI and pre-commit · an MCP server for AI assistants.
How it compares
| DBLift | Flyway Community | Alembic | Your own script | |
|---|---|---|---|---|
Migrations are plain .sql files |
✅ | ✅ | Python revision files | ✅ |
| Runs without a JVM | ✅ | ❌ | ✅ | ✅ |
| Undo | ✅ | paid editions | ✅ downgrade() in Python |
if you wrote it |
| Preview the SQL before applying | ✅ | paid editions | ✅ offline --sql mode |
if you wrote it |
| Detects edited or duplicated migrations | ✅ | ✅ | ❌ | rarely |
| Importable as a Python library | ✅ | ❌ | ✅ | — |
| pytest fixtures | ✅ | ❌ | third-party plugin | — |
| Adopts an existing Flyway history | ✅ | — | ❌ | ❌ |
Alembic is the right choice when your schema is driven by SQLAlchemy models and you want autogenerated revisions. DBLift is for teams who write the SQL themselves.
Databases
20 engines. SQLite needs no driver extra; every other engine has its own, e.g. pip install "dblift[cli,postgresql]".
A bare pip install dblift is the lightweight library install (PyYAML and SQLAlchemy only). dblift[cli] adds the command line (Rich, Jinja2) and SQL analysis (SQLGlot). Python API users can add SQL analysis without the CLI using dblift[analysis]; the API runs without it by default, in execution-only mode, and uses it when you pass analysis_mode="full". dblift[all] installs both plus every database driver.
PostgreSQL · MySQL · MariaDB · SQL Server · Oracle · DB2 · SQLite · DuckDB · CockroachDB · Redshift · Snowflake · Neon · Supabase · Aurora PostgreSQL · AlloyDB · YugabyteDB · TimescaleDB · Citus · Azure Cosmos DB · MongoDB
What each engine supports is in the capability matrix; connection details are under Engines.
What Pro adds
The OSS tier covers the full migration lifecycle: apply, preview, validate state, roll back, and import from Flyway. When review risk grows, the Pro tier adds:
| Feature | Command | What it does |
|---|---|---|
| Schema drift detection | dblift diff |
Compares live database state with what your migrations define. Surfaces objects that have drifted. |
| Schema export | dblift export-schema |
Exports the current schema to SQL migration files. Useful for brownfield onboarding. |
See pricing and Pro features →
Documentation
Everything lives at docs.dblift.com.
Project
- Released on PyPI with trusted publishing — see the changelog and releases.
- Questions, ideas, "would this work for us?" Ask in Discussions.
- Found a bug? Open an issue. Reports with a failing migration file attached get fixed fastest.
- Want to contribute? See CONTRIBUTING.md.
- Security: see SECURITY.md.
- License: Apache 2.0.
If DBLift saved you a JVM or a migrate.sh, a ⭐ helps other teams find it.
Metadata
Release files for dblift 5.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| dblift-5.1.0.tar.gz | 997.5 kB | With provenance | Details for dblift-5.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| dblift-5.1.0-py3-none-any.whl | Python 3 | none | any | Details for dblift-5.1.0-py3-none-any.whl |
Total release size: 2.2 MB
Release files / dblift-5.1.0.tar.gz
| Download URL | dblift-5.1.0.tar.gz |
|---|---|
| Size | 997.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
5c56790eadecd55485ab60a32bf03faa20e3fe7b2e3388d4ded03d109e34a844
|
|
BLAKE2b-256 checksum How to use checksums |
aac2a0f14b8b4737faf2746d2178ca6af0ec23b6d44a98a062eac532b20bd556
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 27, 2026.
Transparency logRelease files / dblift-5.1.0-py3-none-any.whl
| Download URL | dblift-5.1.0-py3-none-any.whl |
|---|---|
| Size | 1.2 MB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
9645508ca6653f003a7d581c4c5ddfd80db91aab563dae551575254b918cefec
|
|
BLAKE2b-256 checksum How to use checksums |
dc39e91b764040de98454127824cb2175abdd6555d266e187cc92ee95fc2c8e9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 27, 2026.
Transparency logSecurity for dblift 5.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for dblift-5.1.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 27, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.