View all files | ||||
DBDiff is an automated database schema and data diff tool for MySQL, Postgres & SQLite. It compares two databases, local or remote, and produces a migration file of the differences automatically.
When used alongside a compatible database migration tool, it can help enable database version control within your team or enterprise.
Other versions may work but are not actively tested. PRs to add official support are welcome.
| MySQL 8.0.x | ✅ Supported |
| MySQL 8.4.x (LTS) | ✅ Supported |
| MySQL 9.3.x (Innovation) | ✅ Supported |
| MySQL 9.6.x (Innovation) | ✅ Supported |
Use --driver=pgsql (or driver: pgsql in your .dbdiff config).
| PostgreSQL 14.x | ✅ Supported |
| PostgreSQL 15.x | ✅ Supported |
| PostgreSQL 16.x (LTS) | ✅ Supported |
| PostgreSQL 17.x | ✅ Supported |
| PostgreSQL 18.x | ✅ Supported |
Use --driver=sqlite. The file path is passed as the database name:
SQLite 3.x is supported (any version supported by the installed pdo_sqlite PHP extension).
--supabase sets driver=pgsql and enables SSL automatically:
The databases below work with DBDiff's existing drivers with no code changes. Unless otherwise noted, these have not been tested by the core team. PRs to add official support are welcome.
| MariaDB 10.x / 11.x | MySQL wire protocol; minor DDL dialect differences |
| AWS Aurora MySQL | Standard MySQL protocol |
| PlanetScale | MySQL-compatible SaaS |
| Vitess / VTGate | MySQL wire protocol via VTGate |
| Percona XtraDB Cluster | MySQL-compatible; Galera replication metadata ignored |
| TiDB | MySQL-compatible; default port 4000 |
| Dolt | MySQL-compatible, version-controlled; CI-tested |
| AWS Aurora PostgreSQL | Standard pgsql connection |
| AWS RDS PostgreSQL | Standard pgsql connection |
| Neon | Standard pgsql; supports branch diffing (see below) |
| AlloyDB (Google Cloud) | Google's Postgres-compatible offering |
| CockroachDB | Postgres wire protocol; some DDL differences |
| YugabyteDB | Postgres-compatible YSQL layer |
| Multigres | Transparent Postgres proxy; no changes needed |
| TimescaleDB | Postgres extension; hypertable DDL diffs natively |
| pgvector | vector(N) columns and HNSW/IVFFlat indexes diff natively |
Neon's copy-on-write branching lets you diff any two branches directly:
Dolt is a MySQL-compatible database with Git-style branching. Each branch is exposed as a separate database:
The quickest way to get started is to download a pre-built release directly from GitHub Releases — no PHP, Node, or Composer required:
| Pre-built binary | ✅ Yes | Quickest start — zero dependencies |
| PHAR | ✅ Yes | Single portable file; requires PHP ≥ 8.1 |
| npm | ✅ Yes (via registry) | Node.js projects or CI pipelines |
| Docker / Podman | — | Isolated environments, CI, or no local PHP |
| Composer (source) | — | Contributing to DBDiff or PHP integration |
PHP requirement: Pre-built binaries, npm packages, and Docker/Podman images bundle PHP 8.3 — no system PHP needed. The PHAR and Composer installs require PHP ≥ 8.1 on your system.
Download from GitHub Releases. No PHP, Node, or Composer required.
| Linux x64 (glibc) | dbdiff-linux-x64 |
| Linux x64 (Alpine / musl) | dbdiff-linux-x64-musl |
| Linux arm64 (glibc) | dbdiff-linux-arm64 |
| Linux arm64 (Alpine / musl) | dbdiff-linux-arm64-musl |
| macOS Apple Silicon | dbdiff-darwin-arm64 |
| macOS Intel | dbdiff-darwin-x64 |
| Windows x64 | dbdiff-win32-x64.exe |
| Windows arm64 | dbdiff-win32-arm64.exe |
After downloading, make it executable (Linux/macOS) and optionally move it to your PATH:
The correct platform binary is selected automatically at install time. Supported: Linux x64/arm64 (glibc + musl), macOS x64/arm64, Windows x64/arm64.
Packages are also published to GitHub Packages as a mirror. If npmjs.org is unavailable:
Download dbdiff.phar from GitHub Releases. Requires PHP ≥ 8.1.
To build a PHAR locally from source, see Building a PHAR.
Pre-built multi-arch images (linux/amd64 + linux/arm64) are published to GHCR on every release. Both Docker and Podman are fully supported — use whichever is available on your system. No local PHP installation is required.
Docker:
Podman (drop-in replacement — commands are identical):
Podman on Linux runs rootless by default — no daemon required. Install via your package manager: sudo apt install podman (Debian/Ubuntu) or brew install podman (macOS).
| latest, {version}, slim-{version} | GHCR | Slim — PHAR + PHP Alpine (~120 MB). For production use / CI. |
| full, full-{version} | GHCR | Full — Composer source install (~600 MB). For development and cross-version testing. |
See DOCKER.md for cross-version testing, Podman setup, and start.sh flags.
Or as a project dependency:
Or globally:
After installing from source, continue with Setup.
For source installs (git clone / Composer) only. Binaries, PHAR, npm, and Docker do not require these steps.
Expected output:
Flags always override settings in .dbdiff.
| --server1=user:pass@host:port | Source connection. Omit if using only one server. |
| --server2=user:pass@host:port | Target connection (if different from server1). |
| --server1-url=<dsn> | Full DSN URL for source (e.g. postgres://user:pass@host:5432/db). |
| --server2-url=<dsn> | Full DSN URL for target. Supported schemes: mysql://, pgsql://, postgres://, postgresql://, sqlite://. |
| --driver=mysql|pgsql|sqlite | Database driver. Defaults to mysql. |
| --supabase | Shorthand for --driver=pgsql + SSL. |
| --format=native|flyway|liquibase-xml|liquibase-yaml|laravel | Output format. Defaults to native. |
| --description=<slug> | Slug used in generated filenames. |
| --template=<path> | Custom output template. |
| --type=schema|data|all | What to diff. Defaults to schema. |
| --include=up|down|both | Directions to include. Defaults to up. (all is accepted as an alias for both.) |
| --nocomments | Strip comment headers from output. |
| --config=<file> | Config file path. Defaults to .dbdiff. |
| --output=<path> | Output file path. Defaults to migration.sql. |
| --memory-limit=<value> | PHP memory limit for this run (e.g. 512M, 1G, 2G, -1 for unlimited). Overrides the 1G default and any memory_limit setting in your config file. |
| --tables=<list> | Comma-separated table include list (supports globs: *, ?). Only these tables are diffed. Example: --tables=users,orders,wp_* |
| --ignore-tables=<list> | Comma-separated table exclude list (supports globs: *, ?). Example: --ignore-tables=cache_*,temp_* |
| --debug | Enable verbose error output. |
| server1.db1:server2.db2 | Databases to compare. Or a single table: server1.db1.table1:server2.db2.table1. |
UP only by default: The generated file includes only the UP (forward) migration by default. To also generate the DOWN (rollback) section, pass --include=all. Example: dbdiff diff --include=all ...
DSN URLs vs --server flags: Use --server1-url / --server2-url when you have a connection string (common with Supabase, Neon, Railway, etc.). Use --server1 / --server2 when specifying credentials separately.
Passwords with special characters: Embed the password percent-encoded in the URL. Use dbdiff url:encode to safely encode any password (see url:encode below). If dbdiff is not yet installed, scripts/encode-password.sh works without any dependencies.
Memory: The CLI sets a default PHP memory limit of 1G. Diffing very large databases may need more — pass --memory-limit=2G on the command line or add memory_limit: 2G to your .dbdiff / dbdiff.yml config. The CLI flag always wins over the config file.
Percent-encodes a raw password for safe embedding in any --server-url connection string.
Capture the result directly into a connection flag:
Accepts stdin too, for use in pipelines:
All characters except RFC 3986 unreserved characters (A–Z a–z 0–9 - _ . ~) are encoded. This is the safe, zero-guesswork approach for any password — including ones containing @, #, ?, /, +, and literal %.
If dbdiff is not yet installed (e.g. you are setting up CI), use the included bash script instead — no Python, Node, or PHP required:
DBDiff includes a built-in migration runner. All migration:* commands accept:
| --db-url=<dsn> | Full DSN URL for the target database. |
| --migrations-dir=<path> | Override the migrations directory. |
| --config=<file> | Path to dbdiff.yml. |
| migration:new <name> | Scaffold a new migration file. | --format=supabase — plain .sql (no DOWN); auto-set inside Supabase projects |
| migration:up | Apply all pending migrations. | --target=<version> — stop after this version |
| migration:down | Roll back applied migration(s). | --last=<n> (default 1), --target=<version> |
| migration:status | Show applied vs pending migrations. Adds a Supa? column inside Supabase projects. | — |
| migration:validate | Verify on-disk checksums match the history table. | — |
| migration:repair | Remove failed entries so they can be retried. | --force — skip confirmation |
| migration:baseline | Mark current DB state as the migration baseline. | --baseline-version=<YYYYMMDDHHmmss>, --description=<text>, --force |
DBDiff supports two on-disk formats in the same directory:
| Native (default) | {version}_{name}.up.sql + optional .down.sql | New projects, rollback support |
| Supabase | {version}_{name}.sql (UP only) | Existing supabase/migrations/ directories |
If both formats exist for the same version timestamp, the native .up.sql file takes precedence.
When DBDiff is run inside (or below) a directory that contains supabase/config.toml, it automatically:
Pass --format=native to migration:new to override the auto-detected format.
If your password contains special characters, use dbdiff url:encode (see url:encode in the Command-Line API section):
When inside a Supabase project (supabase/config.toml present) and the local stack is running (supabase start), --server1-url is resolved automatically from supabase status:
A single dbdiff.yml file in your project root configures both the diff command and the migration runner. Copy dbdiff.yml.example to get started.
Auto-detected filenames, in priority order:
| .dbdiff | Legacy — still supported for backwards compatibility |
| dbdiff.yml | Recommended — YAML syntax highlighting, single file for everything |
| .dbdiff.yml | Hidden-file variant |
| dbdiff.yaml | .yaml extension variant |
You can also pass any filename explicitly: ./dbdiff --config=myconfig.yml server1.db:server2.db
DBDiff offers fine-grained control over what enters the diff. All list values support glob patterns (* matches any characters, ? matches a single character).
| Table include list | tables | --tables | schema + data |
| Table exclude list | tablesToIgnore | --ignore-tables | schema + data |
| Data-only exclude | tablesDataToIgnore | — | data only |
| Column exclusion | fieldsToIgnore | — | schema + data |
| Row filtering | rowsToIgnore | — | data only |
| Per-table scope | tableScope | — | override |
Priority: include list is applied first (only matching tables pass), then the exclude list narrows further. tablesDataToIgnore removes tables from data comparison only. tableScope can override a table to schema, data, or all.
Glob examples: wp_* matches all WordPress tables, *_backup matches any table ending in _backup, log_? matches log_a through log_z.
Comparisons run in this order:
DBDiff supports multiple output formats via --format. Use --description=<slug> to customise generated filenames.
| native (default) | Plain SQL | Any | migration.sql | Up, down, or both |
| flyway | Flyway | Java | V{ts}__{desc}.sql | Down adds U{ts}__{desc}.sql (Flyway Teams) |
| liquibase-xml | Liquibase | Java | changelog.xml | Both directions in one file |
| liquibase-yaml | Liquibase | Java | changelog.yaml | Both directions in one file |
| laravel | Laravel Migrations | PHP | YYYY_MM_DD_HHMMSS_{desc}.php | up()/down() methods |
| (template) | Simple DB Migrate | Python | custom | Use --template=templates/simple-db-migrate.tmpl |
Let us know if you're using DBDiff with other tools so we can add them here.
PHARs are built automatically and attached to every GitHub Release. To build locally from source:
Output: dist/dbdiff.phar — rename and move to /usr/local/bin/dbdiff if desired.
box.json is pre-configured with GZ compression and check-requirements: false so the PHAR works correctly when stitched with the static micro SAPI runtime used in the pre-built binaries.
Test DBDiff locally against any combination of PHP and MySQL:
The CI matrix: 5 PHP × 4 MySQL = 20 jobs, plus dedicated jobs for SQLite, PostgreSQL, DSN URLs, and Supabase.
See DOCKER.md for flags covering fast restarts, recording fixtures, and CI usage.
Please read the Contributing Guide before submitting a PR.
Could you spare 2 minutes to share your feedback?