DDL migration and schema drift detection for DuckLake
Project description
ducktracker
Simple tool to keep your ducks in a row.
DDL migration and schema drift detection for DuckLake.
Install
pip install -e .
For development (includes pytest and ruff):
pip install -e ".[dev]"
Initialize
Create a directory and run init to scaffold the config and migrations folder:
mkdir my-ducklake
ducktracker init my-ducklake --backend ducklake-duckdb
If you omit --backend, you'll be prompted. Two options:
| Backend | Use when |
|---|---|
ducklake-duckdb |
DuckLake catalog in a local DuckDB file. Good for local dev. |
ducklake-postgres |
DuckLake catalog in PostgreSQL. Good for teams. |
init creates ducktracker.toml, migrations/, .gitignore, and a README.md in the target directory. The directory must already exist — init won't create it.
Writing migrations
Two file types, both live in your migrations/ directory:
- Versioned:
V{n}__{description}.sql— applied once, in version order - Repeatable:
R__{description}.sql— re-applied whenever the file content changes
Use create to scaffold the next file:
ducktracker create "create users table"
# → migrations/V2__create_users_table.sql
ducktracker create "refresh views" --repeatable
# → migrations/R__refresh_views.sql
Then open the file and add your DDL.
Running migrations
ducktracker migrate
Flags worth knowing:
--dry-run Show what would be applied without executing any migration SQL
--target N Only apply migrations up to version N (inclusive)
By default, checksums of previously applied versioned migrations are validated before anything runs. If a migration file has been modified after it was applied, migrate will refuse to proceed.
Checking status
ducktracker info
Prints a table with version, description, type, state, applied timestamp, and checksum for every migration it knows about. States: applied, pending, failed, outdated, missing.
To check only checksums without touching the database state:
ducktracker validate
Exits 1 if any applied migration's file has changed since it was applied.
Detecting drift
ducktracker drift
Compares the schema snapshot captured at the last migrate or baseline run against the live catalog. Exits 1 if differences are found — useful in CI to catch out-of-band schema changes.
Output shows added (+), removed (-), and modified (~) objects.
Baselining an existing database
If you're bringing an existing database under ducktracker management, use baseline before running any migrations:
ducktracker baseline --version 3
ducktracker baseline --version 3 --description "after initial setup"
This captures the current schema as a snapshot and marks the database at the given version. It will refuse to run if the history table already contains any records (including a prior baseline).
Configuration
ducktracker looks for ducktracker.toml in the current directory by default. Override with -c:
ducktracker -c path/to/ducktracker.toml migrate
A minimal config looks like this:
[connection]
catalog_name = "my_ducklake"
catalog_backend = "duckdb"
duckdb_metadata_path = "ducklake_metadata.db"
[migrations]
directory = "migrations"
target_schema = "main"
[behavior]
validate_on_migrate = true
out_of_order = false
Using a DuckDB secret
Instead of putting connection details directly in the config file, you can reference a named DuckDB persistent secret. Set secret_name in place of duckdb_metadata_path (duckdb backend) or postgres_connection (postgres backend):
[connection]
catalog_name = "my_ducklake"
catalog_backend = "postgres"
secret_name = "my_ducklake_secret"
The secret must already exist in DuckDB before ducktracker runs.
If your secrets are stored outside DuckDB's default location (~/.duckdb/stored_secrets/), add secret_directory to the same [connection] block:
[connection]
catalog_name = "my_ducklake"
catalog_backend = "postgres"
secret_name = "my_ducklake_secret"
secret_directory = "/run/secrets/duckdb"
ducktracker issues SET secret_directory before opening any catalog connection, so the path is in effect for the entire session.
Config layers
Settings are resolved in this order, with later layers winning:
ducktracker.toml- Environment variables
- CLI flags
Key environment variables
| Variable | Config equivalent |
|---|---|
DUCKTRACKER_CATALOG_NAME |
connection.catalog_name |
DUCKTRACKER_CATALOG_BACKEND |
connection.catalog_backend |
DUCKTRACKER_DUCKDB_METADATA_PATH |
connection.duckdb_metadata_path |
DUCKTRACKER_POSTGRES_CONNECTION |
connection.postgres_connection |
DUCKTRACKER_SECRET_DIRECTORY |
connection.secret_directory |
Global CLI flags
These apply to all commands except init:
-c, --config PATH Path to ducktracker.toml
--catalog NAME Override catalog name
--backend TYPE Override backend (duckdb, postgres) — note these differ from init's backend values
--metadata PATH Override DuckDB metadata file path
--connection STRING Override PostgreSQL connection string
--secrets-dir PATH Override DuckDB persistent secrets directory
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ducktracker-0.1.0.tar.gz.
File metadata
- Download URL: ducktracker-0.1.0.tar.gz
- Upload date:
- Size: 29.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c2fb093016b9d24401c8b3c7d20dff3440f619ee868565d339a67b372ca39a78
|
|
| MD5 |
ab422aa196f5a9b777420be28f052135
|
|
| BLAKE2b-256 |
ad13a2065dca228803ae97855988e90e52bd5dc6d7f1ad5f7c0cdadec8a77bb2
|
Provenance
The following attestation bundles were made for ducktracker-0.1.0.tar.gz:
Publisher:
release.yml on nah-a/ducktracker
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ducktracker-0.1.0.tar.gz -
Subject digest:
c2fb093016b9d24401c8b3c7d20dff3440f619ee868565d339a67b372ca39a78 - Sigstore transparency entry: 1106516722
- Sigstore integration time:
-
Permalink:
nah-a/ducktracker@1673d4d40ff1c7560e18b74df94ddbe3a8df38f8 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/nah-a
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@1673d4d40ff1c7560e18b74df94ddbe3a8df38f8 -
Trigger Event:
pull_request
-
Statement type:
File details
Details for the file ducktracker-0.1.0-py3-none-any.whl.
File metadata
- Download URL: ducktracker-0.1.0-py3-none-any.whl
- Upload date:
- Size: 29.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3bfbf1664cd577c22018cc81df53a9582820f20efcf6641f5032b151fe65394b
|
|
| MD5 |
68ef44bca657e5337856980c631fdbdb
|
|
| BLAKE2b-256 |
229b39d30ffa8da3b0f4d8290c9a1933ae93896819c965d22dec88a1bc2f3cbb
|
Provenance
The following attestation bundles were made for ducktracker-0.1.0-py3-none-any.whl:
Publisher:
release.yml on nah-a/ducktracker
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ducktracker-0.1.0-py3-none-any.whl -
Subject digest:
3bfbf1664cd577c22018cc81df53a9582820f20efcf6641f5032b151fe65394b - Sigstore transparency entry: 1106516809
- Sigstore integration time:
-
Permalink:
nah-a/ducktracker@1673d4d40ff1c7560e18b74df94ddbe3a8df38f8 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/nah-a
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@1673d4d40ff1c7560e18b74df94ddbe3a8df38f8 -
Trigger Event:
pull_request
-
Statement type: