A Modern Alembic Alternative
for SQLAlchemy.
Looking for an Alembic alternative for SQLAlchemy? dbwarden is a declarative database migration tool where your SQLAlchemy models remain the source of truth and migrations are derived from schema changes. What reaches the pull request is plain SQL: readable by any DBA, executable without the Python runtime, and verified against the database before it ships.
| Concern | dbwarden | Alembic |
|---|---|---|
| Source of truth | SQLAlchemy models | Revision chain |
| Artifact | Plain SQL with upgrade + rollback | Python revision script |
| Rollback | Generated contract, placeholder refused | Author-defined downgrade() |
| Runtime | SQL runner or any SQL system | Python + Alembic runtime |
| Offline generation | Committed model state, no DB needed | Offline SQL rendering |
| SQLAlchemy integration | Yes | Yes |
| Model-driven generation | Diff from models or committed state | Autogenerate from a live database |
| Models as schema authority | Yes: models define the schema | No: the revision chain is authoritative |
| Safety analysis | INFO / WARNING / ERROR classifier | Not built-in |
| Impact analysis | AST + grep scan of application code | Not built-in |
| Correctness verification | Convergence gate + round-trip verification | Not built-in |
| Schema state | Checksummed snapshots + model state | Revision-based (version table) |
| Drift detection | Deterministic diff from snapshots or live DB | Implicit via autogenerate |
| FastAPI integration | Official plugin with sessions + health | Mature ecosystem (third-party) |
| Rename detection | Explicit --rename flag, never guesses | Drop + create unless caught in review |
| Repeatable migrations | Runs-always + runs-on-change classes | Not built-in |
| Seed management | Code + file seeds with rollback | Not built-in |
| Multi-database | Unified config with per-database isolation | Per-database env.py setup |
dbwarden is for SQLAlchemy teams that want models to remain the source of truth and migrations to become plain SQL receipts (readable by anyone, executable without the application runtime). Alembic is for teams that want an established imperative revision system, maximum Python-level control, and the broadest existing SQLAlchemy migration ecosystem. Neither is automatically better; they make different bets.
revision = "ae1027a6acf"
down_revision = "1975ea83b712"
def upgrade():
op.add_column("users", sa.Column("bio", sa.Text()))
def downgrade():
op.drop_column("users", "bio")class User(Base):
bio = Column(Text, nullable=True)
-- upgrade
ALTER TABLE users ADD COLUMN bio TEXT;
-- rollback
ALTER TABLE users DROP COLUMN bio;The same change, twice: a Python program that must run inside Alembic, and a SQL file that any database tool can execute. Most of the differences below come from that one distinction. Most migration workflows describe the schema twice: once in the models, once in the migration scripts, and nothing checks the two stay in agreement. The disagreement usually turns up in production, at the moment the database is asked to change.
SQLAlchemy models define what the schema should be. Generated migration files are artifacts of that state: useful for review and deployment, not a second schema to maintain. The generated SQL is committed, reviewed, and deployed, but it is output; old migrations stay useful for review and deployment, but they no longer define the database. In Alembic, each revision describes the transition from one version to the next, and the chain becomes the effective definition of the schema. Models and the chain are two representations, and keeping them aligned is the team's responsibility.
The file in the pull request contains the SQL that will run, with upgrade and rollback sections together. A DBA or any SQL-capable deployment system can execute it. Alembic's revision contains upgrade and downgrade functions. You can inspect rendered SQL, but the artifact itself is Python executed through Alembic's runtime.
Executable rollback is generated with the upgrade. Placeholder rollback is refused by default, and irreversible changes must be declared explicitly and visibly. In Alembic, every revision can define a downgrade, but whether it exists, works, and stays correct is left to the author and the review process.
Risk shows up at different points. dbwarden's snapshots make diffs deterministic, explicit rename flags prevent ambiguous drop-and-create behavior, and impact analysis can inspect application references before destructive changes ship. Alembic's autogenerate is useful, but the generated revision is still a candidate Python file that must be checked and corrected by the team.
# edit models.py
$ alembic revision --autogenerate "add bio"
# review revision script
# write/verify downgrade()
# commit revision + model
$ alembic upgrade head# edit models.py
$ dbwarden make-migrations "add bio"
# review generated SQL
# commit model + migration SQL
$ dbwarden migrateBoth workflows are short. Over time the difference compounds: an Alembic revision keeps its author's hand-edits forever, while dbwarden regenerates the migration from the models and keeps review attached to the pull request.
Setup differs too. alembic init creates alembic.ini, env.py, a versions directory, and a script template. dbwarden uses one project-root declaration; model discovery is automatic, and model_paths can constrain it.
from dbwarden import DbwardenDatabase
class Primary(DbwardenDatabase):
database_name = "primary"
default = True
database_type = "postgresql"
database_url_sync = "postgresql://user:password@localhost:5432/primary"
model_paths = ["app"]Generation is where the declarative model pays off. Alembic's autogenerate depends on a live database, and documented limits include renames and some constraint changes; a rename can become a drop and create unless a reviewer catches it. dbwarden uses checksummed snapshots for deterministic diffs, refuses to guess ambiguous renames, and supports a safe multi-step type-change workflow.
$ dbwarden make-migrations "rename name to full_name" \
--rename users.name:full_name
# preview without writing
$ dbwarden make-migrations "add bio" --plan --sqlAlembic runs Python revision programs through alembic upgrade head, with targets, relative steps, and stamp for an existing database. That is a complete system, but the target needs the project runtime and env.py. dbwarden reads pending SQL files and applies them in order; --baseline is the stamp equivalent, and history, status, --count, --to-version, and --all cover the operational workflow. Versioned SQL can also be handed directly to psql or a DBA.
$ dbwarden migrate
$ dbwarden migrate --count 2
$ dbwarden migrate --to-version 0007
$ dbwarden migrate --baseline --to-version 0005
$ dbwarden migrate --allExecution has guardrails: migration locks prevent concurrent runners, --dry-run previews work, --with-backup captures a pre-migration state, and --sandbox replays changes before production.
Alembic revisions are programs. Applying them requires the target environment to have Python, the project dependencies, the migration package, and a working env.py configuration. That flexibility is valuable when a migration needs arbitrary Python or data transformations. dbwarden migrations are SQL artifacts: dbwarden migrate is a convenient runner, not the definition of the deployment environment.
Alembic's version table records which revisions ran, not the complete structural state of the database. dbwarden compares models against live state on generation and also supports checksummed snapshots and exported model state, which makes offline generation possible: export once, commit the state file, and generate in CI with no database service.
$ dbwarden export-models --database primary
$ git add .dbwarden/model_state.primary.json
$ dbwarden make-migrations "add bio column" --offline
$ dbwarden check-impact
$ dbwarden migrate --sandboxMigrating from Alembic is a non-destructive, six-step process. Your models stay exactly where they are and your database is never rebuilt; you are replacing the migration workflow, not the schema.
Install and configure. uv add dbwarden, create dbwarden.py. This replaces alembic.ini and env.py.
Generate the baseline. make-migrations "baseline from alembic" emits the full schema from your models.
Baseline the database. migrate --baseline records the migration as applied without running it.
Verify convergence. status, check, and diff must all be clean.
Offline CI (optional). export-models, commit the state file, and generate offline in CI.
Retire Alembic. Remove alembic.ini, env.py, and versions/ from the active workflow.
$ dbwarden init
$ dbwarden make-migrations "baseline from alembic"
$ dbwarden migrate --baseline
$ dbwarden status
$ dbwarden diffRead the full migration guide ↗Alembic is the better call for Python data migrations, arbitrary application logic inside revisions, branching and merging revision graphs, older Python or SQLAlchemy environments, and teams that want manual control over every migration step (dbwarden's manual files are SQL by design). For a practical tiebreaker, inspect the last ten migrations: mostly pure schema changes suggest declarative tooling will remove recurring work; Python backfills and application-level transformations are imperative by nature, and Alembic is built for them.
Choose dbwarden when you want SQLAlchemy models as the single source of truth, plain SQL readable by any DBA, and rollback as a generated contract rather than a manual promise. Safety classification, impact analysis, and correctness verification come with the workflow.
Choose something else when Python data migrations are central to your workflow, you rely on branching and merging revision graphs, or you want imperative control over every migration step, including arbitrary Python inside upgrade and downgrade. Teams with deep Alembic expertise and mostly data-transformation workloads can stay with what they know.
Limits are part of the contract on both sides. Rollback protects schema shape, not deleted data. Enum additions, some partition changes, and unsupported operations may require an explicit irreversible marker or a manual migration. MySQL DDL is non-transactional and MariaDB does not provide full round-trip introspection. A warning is a review boundary, not a guarantee that a change is safe.
Switch from Alembic, step by step ↗Read the migration guide ↗Is dbwarden a drop-in replacement for Alembic? Not exactly. dbwarden is a different approach: models as the schema authority, plain SQL as the artifact, generated rollback alongside upgrade. The migration workflow changes, but your models stay where they are.
What happens to the existing Alembic history? It stays in git, which is where old revisions belong. make-migrations "baseline from alembic" emits the full current schema from your models, and migrate --baseline records it as applied without running any DDL.
Can dbwarden and Alembic run side by side? Yes. They track state in separate tables, so both tools can coexist during a transition period. Generate the same change with both and compare the SQL until you are confident.
What is the main advantage of switching? Models remain the single source of truth. Generated SQL is readable by anyone, rollback is a contract rather than a convention, and the database is verified against the models rather than trusted from a version table.