Confiture 🍓
PostgreSQL migrations, sweetly done.
Build from DDL. Adopt on day one against a database that already has migrations applied. Preflight every deploy against a parallel database with structural diff. Sync production data with PII anonymization.
In 30 seconds
# 1. You already have a database at migration 004 (applied by hand or by another tool).
# Tell Confiture about that history without re-running the SQL:
$ confiture migrate baseline --through 004 -c db/environments/production.yaml
✅ 001 create_users (marked as applied)
✅ 002 create_orders (marked as applied)
✅ 003 add_user_email (marked as applied)
✅ 004 add_user_preferences (marked as applied)
✅ Marked 4 migration(s) as applied, skipped 0 already applied
# 2. Machine-readable proof that the tracking is healthy:
$ confiture migrate status -c db/environments/production.yaml --format json | jq '.applied | length'
4
# 3. Preflight: replay pending migrations on a parallel DB, emit a structural diff vs. db/schema/.
$ confiture migrate preflight --against "$PREFLIGHT_URL" -c db/environments/production.yaml
▸ Replaying pending migrations on preflight DB …
✓ 20260520143015_add_user_bio applied in 24 ms
▸ Comparing resulting schema vs. db/schema/ …
✓ No drift — preflight matches db/schema/
✓ Preflight passed. Safe to deploy.
exit 0
That's the loop. Baseline once → status to confirm → preflight every deploy.
Already have migrations?
The single biggest reason migration tools fail adoption is the day-one cliff: existing tables already exist, so any tool that tries to apply migrations from scratch crashes on the first CREATE TABLE. Confiture's answer is migrate baseline:
confiture migrate baseline --through <last-applied-version>
The walkthrough — including failure modes, the integration test that backs the recipe, and what tb_confiture ends up looking like — is in docs/guides/legacy-bootstrap.md.
No db/schema/ directory? That works too.
confiture migrate up, down, down-to, status, current, baseline,
and preflight are the migration runner — they don't require a db/schema/
directory (migrate current prints the latest applied revision as a narrow
"what's deployed?" contract; migrate down --steps N rolls back relatively
while migrate down-to <revision> rolls back to a specific revision, refusing
atomically if any required .down.sql is missing). The
"Build from DDL" pitch above the fold sells one of confiture's four
strategies; the other three (incremental migrations, production sync,
schema-to-schema FDW migration) work against a project whose only source
of truth is the migration chain itself.
If you're evaluating confiture against Flyway / Alembic / dbmate / sqlx-cli
as a pure migration runner, skip confiture build and use everything else.
Walkthrough: docs/guides/02-incremental-migrations.md.
When to use Confiture?
| Capability | Confiture | Flyway | Alembic | dbmate | sqlx-cli | plain psql |
|---|---|---|---|---|---|---|
| Source of truth | DDL files or migration chain | migration chain | model classes | migration chain | migration chain | DDL files |
| Tracking table | yes | yes | yes | yes | yes | no |
Rollback (down.sql) |
yes | paid | yes | yes | yes | no |
| Preflight against a copy DB | yes (structural diff) | no | no | no | no | no |
| Build from scratch in <1s | yes | no | no | no | no | yes (manual) |
| Production sync + anonymization | yes | no | no | no | no | no |
| Zero-downtime via FDW | yes | no | no | no | no | no |
| Multi-agent coordination | yes | no | no | no | no | no |
| Ecosystem maturity / stars | early | very mature | mature | mature | mature | n/a |
Note on "source of truth": confiture can run as a pure migration tool against a project that has no
db/schema/directory — the DDL workflow is opt-in. See Nodb/schema/directory? above.
Confiture wins on build-from-DDL, structural-diff preflight, production sync, and multi-agent coordination. It loses on ecosystem age — Flyway and Alembic have a decade of community knowledge. Pick honestly.
Adoption checklist
| Situation | Recommended tool |
|---|---|
| 1 environment + 1 contributor, schema rarely changes | plain psql |
| 2+ environments, schema changes weekly | Confiture, Flyway, Alembic, or dbmate |
| Multi-agent / AI-driven development on shared schemas | Confiture |
You have a migration chain (no db/schema/) and want preflight + tracking |
Confiture (use everything except confiture build) |
You want db/schema/ to be source of truth, not a migration chain |
Confiture |
You need zero-downtime schema swaps with postgres_fdw |
Confiture (Medium 4) |
| You're committed to SQLAlchemy ORM | Alembic |
| You're committed to a JVM stack | Flyway |
CI integration
A migrate preflight gate on every PR, a migrate up step on deploy. Exit codes are semantic, so the CI configuration stays simple:
# .github/workflows/db.yml
name: DB
on:
pull_request:
paths:
- 'db/**'
push:
branches: [main]
jobs:
preflight:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
env: { POSTGRES_PASSWORD: x }
ports: ['5432:5432']
options: >-
--health-cmd pg_isready --health-interval 10s
--health-timeout 5s --health-retries 5
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v3
- run: uv pip install --system fraiseql-confiture
- name: Restore production snapshot to preflight DB
run: ./scripts/restore-snapshot.sh # your own; pg_restore from S3/GCS
- name: Confiture preflight
env:
PREFLIGHT_URL: postgresql://postgres:x@localhost:5432/preflight
run: |
confiture migrate preflight \
--against "$PREFLIGHT_URL" \
-c db/environments/preflight.yaml \
--format json --output preflight.json
- uses: actions/upload-artifact@v4
with:
name: preflight-report
path: preflight.json
deploy:
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v3
- run: uv pip install --system fraiseql-confiture
# No YAML needed in CI — the migrate family reads DATABASE_URL directly
# (or pass --database-url "$DSN"). See the connection-source docs below.
- run: confiture migrate up
env:
DATABASE_URL: ${{ secrets.PROD_DATABASE_URL }}
migrate up/down/status/verify/preflight accept --database-url <dsn> (or read CONFITURE_DATABASE_URL / DATABASE_URL) so runtime-resolved DSNs need no temp YAML — precedence and details in the CLI reference.
Exit codes are a documented stability contract — see the exit-code reference. The most operationally important: 2 tracking table absent, 3 DB connection failed, 5 config invalid, 6 lock contention. For migrate preflight's drift-gate codes specifically, see the dry-run guide.
Migrations that open their own SAVEPOINTs, use
psycopg'sconn.transaction(), or wrapDO $$ … EXCEPTION WHEN … $$blocks are supported under all three modes. The rules a migration body must follow for the SAVEPOINT-based rollback to stay clean are documented in the transaction & SAVEPOINT contract.
Python project snippet
Add Confiture as a dev dependency. pglast (PostgreSQL's own parser) comes with it since 0.50.0.
# pyproject.toml
[dependency-groups]
dev = [
"fraiseql-confiture>=0.50",
"pytest>=8",
]
# justfile
default:
just --list
db-build:
confiture build --env local
db-up:
confiture migrate up
db-status:
confiture migrate status
db-preflight:
confiture migrate preflight --against "$PREFLIGHT_URL"
Or as a Makefile:
db-build:
confiture build --env local
db-up:
confiture migrate up
db-status:
confiture migrate status
Library API
Confiture is a CLI first, but the migrator is fully usable from Python:
from confiture import Migrator
with Migrator.from_config("db/environments/prod.yaml") as m:
status = m.status()
if status.has_pending:
result = m.up()
print(f"Applied {len(result.applied)} migrations")
The Four Strategies
| Strategy | Use Case | Command |
|---|---|---|
| Build from DDL | Fresh databases, testing, CI | confiture build --env local |
| Incremental Migrations | Existing databases, production | confiture migrate up |
| Production Sync | Copy data with PII anonymization | confiture sync --from prod --anonymize users.email |
| Zero-Downtime | Complex migrations via FDW | confiture migrate schema-to-schema |
Documentation
Start here
- Getting started — first 5 minutes.
- Legacy bootstrap guide — adopting on an existing database.
- Prerequisites — PostgreSQL version, roles, secret stores.
Guides
- Build from DDL
- Incremental Migrations —
up,down, rollback. - Parallel, cacheable CI provisioning —
build --dumpartifacts,test-dbtemplate/clone, per-worker xdist fixtures, slim seed profiles. - Production Data Sync
- Zero-Downtime Migrations
- Dry-Run + Preflight
- Replica-safe migrations —
replica_001/PFLIGHT_REPLICA_*forward-compatibility lint under streaming replication. - Bootstrap —
confiture bootstrapfor one-shot env ownership setup. - Superuser Migrations —
requires_superuser = True+migrate apply-asrecovery workflow. - Function Uniqueness —
func_001catches duplicateCREATE FUNCTIONacross DDL files. - Security-Definer Lint —
sec_002flagsSECURITY DEFINERfunctions/procedures that don't pinsearch_path(CVE-2018-1058); static DDL scan and livepg_procpath;--emit-remediationgenerates ALTER scripts. - Named Schemas
- Hooks
- Multi-Agent Coordination
Reference
- Tracking table (
tb_confiture) - Transaction & SAVEPOINT contract — what migration bodies may and may not do under the wrapping transaction.
- Exit-code convention — the stable, wrapper-facing exit codes.
- CLI
- Configuration YAML
- Complete feature list
For agents and tooling
- JSON schemas are published for the
--format jsonoutput ofbuild,drift,introspect,lint,sync,validate-config,verify-checksumsand, in the migrate family,migrate up,migrate down-to,migrate status,migrate current,migrate diff,migrate fix,migrate introspect,migrate preflight,migrate steps,migrate validateandmigrate verify. They ship in the package (python/confiture/schemas/) and are mirrored underdocs/reference/json-schemas/; a test asserts the mirror equals the packaged source (seedocs/reference/json-schemas.md). The other commands' JSON payloads are stable but not schema-backed yet. - On an error path in
--format jsonmode, the migrate family emits a structured error envelope on stdout —{"ok": false, "error": {code, message, severity, actionable, details, migration, file, line}}— and exits with the exit code for that error. The full code list and the envelope schema are in the error-code codebook. confiture migrate validate --list-patterns --format jsonexposes the full idempotency-detection catalog (read-only, no DB / config / migrations directory needed).- Quiet-success ambiguities surface advisory hints in
payload["hints"](or on stderr in text mode) — exit codes are unaffected.
Contributing
git clone https://github.com/fraiseql/confiture.git
cd confiture
uv sync --all-extras
uv run pytest
See CONTRIBUTING.md and CLAUDE.md.
Author & License
Vibe-engineered by Lionel Hamayon 🍓
MIT License — Copyright (c) 2025 Lionel Hamayon
Making jam from strawberries, one migration at a time. 🍓→🍯
Release files for fraiseql-confiture 1.11.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 | |
|---|---|---|---|
| fraiseql_confiture-1.11.0.tar.gz | 3.0 MB | Details |
Built distributions (wheels)
Total release size: 16.4 MB
Release files / fraiseql_confiture-1.11.0.tar.gz
| Download URL | fraiseql_confiture-1.11.0.tar.gz |
|---|---|
| Size | 3.0 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d73afb78c013aa58159c20d8155968c639d7d3eb49e6f7824de09e8d472a215d
|
|
BLAKE2b-256 checksum How to use checksums |
cabb744188f9b5b398eb78d3cc0d84b09f12f17d9fc726d6fac9ea5c07dfb62b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.16 {"installer":{"name":"uv","version":"0.12.16","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / fraiseql_confiture-1.11.0-cp314-cp314-win_amd64.whl
| Download URL | fraiseql_confiture-1.11.0-cp314-cp314-win_amd64.whl |
|---|---|
| Size | 1.3 MB |
| Tags | CPython 3.14 Windows x86-64 |
|
SHA-256 checksum How to use checksums |
bd81f5bfe0e5cbe19a990c6176f95260756682cdef8b63c5a2ee1724cd296782
|
|
BLAKE2b-256 checksum How to use checksums |
0e6269364fcb695745cfc9a98852f0c54089a83eee932ccaa1c615bef07fbb85
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.16 {"installer":{"name":"uv","version":"0.12.16","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / fraiseql_confiture-1.11.0-cp313-cp313-win_amd64.whl
| Download URL | fraiseql_confiture-1.11.0-cp313-cp313-win_amd64.whl |
|---|---|
| Size | 1.3 MB |
| Tags | CPython 3.13 Windows x86-64 |
|
SHA-256 checksum How to use checksums |
15a836242c2cff56d173a711091a8579b2ce73ca5901c45874b89b8f4e27efcf
|
|
BLAKE2b-256 checksum How to use checksums |
621cf5e3a8220f3cf9fcf7b5ed757861c2630505872c0bcc2c8cc5c51a0f0693
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.16 {"installer":{"name":"uv","version":"0.12.16","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / fraiseql_confiture-1.11.0-cp313-cp313-manylinux_2_28_x86_64.whl
| Download URL | fraiseql_confiture-1.11.0-cp313-cp313-manylinux_2_28_x86_64.whl |
|---|---|
| Size | 1.4 MB |
| Tags | CPython 3.13 Linux glibc 2.28+ x86-64 |
|
SHA-256 checksum How to use checksums |
d4d15635ca52edc4475194723fe0677f602529200060693f0a2648b172d7c1a7
|
|
BLAKE2b-256 checksum How to use checksums |
95c3edfec5bd922c2540f4d2d6a6ea87d163f02d49c8c876ae7e60cab70614ac
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.16 {"installer":{"name":"uv","version":"0.12.16","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / fraiseql_confiture-1.11.0-cp313-cp313-macosx_11_0_arm64.whl
| Download URL | fraiseql_confiture-1.11.0-cp313-cp313-macosx_11_0_arm64.whl |
|---|---|
| Size | 1.3 MB |
| Tags | CPython 3.13 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
b2323d543bb618da8202362c5e69a7710f094a9f55d86df2e1eb23b99ae9ba55
|
|
BLAKE2b-256 checksum How to use checksums |
51ef5f3bfaf0bba0f193cb2a4263530c5ea95732de82e6461ec6ca32784aedba
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.16 {"installer":{"name":"uv","version":"0.12.16","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / fraiseql_confiture-1.11.0-cp312-cp312-win_amd64.whl
| Download URL | fraiseql_confiture-1.11.0-cp312-cp312-win_amd64.whl |
|---|---|
| Size | 1.3 MB |
| Tags | CPython 3.12 Windows x86-64 |
|
SHA-256 checksum How to use checksums |
e22edcab751af1e51998b8d0cb6005f9717bdb9a8259c7215acc47e2a6062a0c
|
|
BLAKE2b-256 checksum How to use checksums |
d90a962d97b506fe198eea79e02ba827d6d11cfe0dfb19c0bf025df75331e2bb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.16 {"installer":{"name":"uv","version":"0.12.16","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / fraiseql_confiture-1.11.0-cp312-cp312-manylinux_2_28_x86_64.whl
| Download URL | fraiseql_confiture-1.11.0-cp312-cp312-manylinux_2_28_x86_64.whl |
|---|---|
| Size | 1.4 MB |
| Tags | CPython 3.12 Linux glibc 2.28+ x86-64 |
|
SHA-256 checksum How to use checksums |
f143eb0cf1ce0d5f82f9ca7357266aea5b6490b644add12b9eb78482a37530c9
|
|
BLAKE2b-256 checksum How to use checksums |
d2a129739ca156535f101e03890eb22c9613108b1e28249bcc519366a8654f7d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.16 {"installer":{"name":"uv","version":"0.12.16","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / fraiseql_confiture-1.11.0-cp312-cp312-macosx_11_0_arm64.whl
| Download URL | fraiseql_confiture-1.11.0-cp312-cp312-macosx_11_0_arm64.whl |
|---|---|
| Size | 1.3 MB |
| Tags | CPython 3.12 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
c961066a68dfd2e0765abe0ef33709143e40abefcc19a96f901a5b9d89b21db0
|
|
BLAKE2b-256 checksum How to use checksums |
14e38eadc5053578a4d945850d699ef7e2819a2711300f8d982d953efbb6e78d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.16 {"installer":{"name":"uv","version":"0.12.16","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / fraiseql_confiture-1.11.0-cp311-cp311-win_amd64.whl
| Download URL | fraiseql_confiture-1.11.0-cp311-cp311-win_amd64.whl |
|---|---|
| Size | 1.3 MB |
| Tags | CPython 3.11 Windows x86-64 |
|
SHA-256 checksum How to use checksums |
4cf77c1a8cb602194804aba4065e9e27b4c3a18f4f8112a18472b59d92236931
|
|
BLAKE2b-256 checksum How to use checksums |
3251fdd8dae9122e2aae7dc984cd26f303375d5ed0df4c1d081cec3547563a45
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.16 {"installer":{"name":"uv","version":"0.12.16","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / fraiseql_confiture-1.11.0-cp311-cp311-manylinux_2_28_x86_64.whl
| Download URL | fraiseql_confiture-1.11.0-cp311-cp311-manylinux_2_28_x86_64.whl |
|---|---|
| Size | 1.4 MB |
| Tags | CPython 3.11 Linux glibc 2.28+ x86-64 |
|
SHA-256 checksum How to use checksums |
61bda16fcf6ef32d61bc1f8a4aca4d6d54d5f766efeb9d8779c3f2951f175ea1
|
|
BLAKE2b-256 checksum How to use checksums |
508e1ea83aba330987af39b71e5bee841300840dbc50dcc5360429ef2a97ed83
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.16 {"installer":{"name":"uv","version":"0.12.16","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / fraiseql_confiture-1.11.0-cp311-cp311-macosx_11_0_arm64.whl
| Download URL | fraiseql_confiture-1.11.0-cp311-cp311-macosx_11_0_arm64.whl |
|---|---|
| Size | 1.3 MB |
| Tags | CPython 3.11 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
65d76d03be7fd6c74c802e6847a093f856c11e7f99a302c95893268116baf76a
|
|
BLAKE2b-256 checksum How to use checksums |
767901fce913b07a666999b56cd51bde24ff4fd8932830d1b66df329dd17aa4a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.16 {"installer":{"name":"uv","version":"0.12.16","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|