10 Reasons to Choose Ptah over Atlas
Atlas Pro-like workflows in an open-source stack you can host yourself: schema checks, private artifacts, reviewed AI changes, and no third-party telemetry.
Ptah helps you plan, review, and apply database migrations. Try in your browser
Your team wants schema review, migration checks, and controlled deployment. It also wants to choose where artifacts live, who receives execution data, and which services must be reachable during a release.
For that team, we recommend Ptah: Atlas Pro-like workflows in a fully self-hosted, sovereign, open-source stack, with no third-party telemetry. You can operate the migration runner, registry, and deployment controller in your own infrastructure. AI workflows can follow the same pattern when you also host the model and its client.
We reran the Atlas handover, view drift, and index-lock experiments with Ptah 0.13.0 and PostgreSQL 18.6. The linked MCP example also uses 0.13.0; the other walkthroughs retain their recorded version pins. Each links to its actual commands and results. Our Atlas execution tests used source-built CE v1.3.0. Pro comparisons describe official documentation checked on October 7, 2026; we did not run its commercial binary. “Pro-like” describes overlapping workflows, not complete feature or driver parity.
1. The implementation stays open source
Section titled “1. The implementation stays open source”With Ptah, the schema and migration implementation is available under the MIT license. You can retain a version, build it, and review changes before adopting them. The license boundary documents Ptah’s independent implementation and the separately licensed compatibility material.
Atlas CE is open source too. The difference is where the advanced workflow lives: Atlas’s feature matrix reserves features such as PostgreSQL view support and table-locking checks for Pro. Ptah’s examples below exercise those kinds of work in its open-source implementation. That matters when source availability is a requirement for the whole workflow.
2. Local migration work does not need a vendor account
Section titled “2. Local migration work does not need a vendor account”When rerunning the Atlas handover, we put PostgreSQL and both CLIs inside a container with no external network interface. Ptah continued the migration history, inspected the database, and compared the schema using only the local files and database. No account token or online license grant was part of that path.
The tested CE commands also worked offline. This is a reason to choose Ptah when you need its additional capabilities under the same operating model, not a claim that every Atlas command needs internet access. Atlas documents offline licensing options for its commercial product; their conditions need a separate evaluation.
3. No third-party telemetry is part of the Ptah workflow
Section titled “3. No third-party telemetry is part of the Ptah workflow”Ptah does not automatically report your CLI usage to a vendor analytics service. The default build’s tracing integration is inactive. Its optional observability build can export traces to an endpoint you explicitly configure. You can keep that endpoint in your own monitoring infrastructure.
That is the meaning of no third-party telemetry here. Your own logs and metrics remain useful. Requests to a model, database, or registry you select are functional data transfers and still need an appropriate destination. The data-boundary guide explains the embedding case; the Assist example shows the prompt and tool-result boundary.
Atlas’s CLI privacy documentation describes anonymous telemetry with an opt-out and separate identified-user telemetry for logged-in Pro accounts. It lists command and runtime details, including hostname and licensing identifiers. It also says database contents and passwords are not collected. We use that documented distinction when comparing the products; the isolated CE run did not measure commercial telemetry.
4. A changed view can fail your schema check
Section titled “4. A changed view can fail your schema check”Our payment queue depended on app.unpaid_invoices. We changed its predicate
so it selected paid invoices instead. A SQL client confirmed the wrong queue.
Ptah then ran:
ptah schema compare --schema-file schema.sql --schemas app --exit-codeOn 0.13.0, its recorded output included:
views_modified (1): app.unpaid_invoices SELECT id,The command exited 1. The source-built CE inspection omitted the view,
and its diff reported synchronized schemas. Both tools could execute the
CREATE VIEW SQL; the difference was whether this check represented the view.
Atlas documents view support in Pro. Ptah provided that check in the tested
open-source release. The complete example
shows the wrong result, detection, and restoration. The 0.13.0 run
records the same outcome: the wrong queue contained invoice 2; after
restoration it contained invoices 1 and 3, and the check returned 0.
5. PostgreSQL deployment checks run beside your SQL
Section titled “5. PostgreSQL deployment checks run beside your SQL”A migration that builds an ordinary index can block writers. Ptah’s linter reported PG101 for that case, and our database experiment reproduced the blocked insert. A concurrent index migration allowed the insert to finish. We made warnings fail the review gate with:
ptah migrations lint --dir index-blocking --dialect postgres --fail-on anyOn 0.13.0, this returned 1 with PG101 for the up migration and PG106 for the down migration. The ordinary index queued a new writer until its three-second lock timeout. With the concurrent index, the writer succeeded while the index waited. Both indexes became valid after the older transaction committed. This small fixture tests locking behavior, not build performance.
The linter walkthrough also tests lost column values and a failing required-column change. These checks run locally against your migration files. Atlas’s lint rule matrix places PostgreSQL table-locking and concurrent-index checks in Pro. Its CE linter did catch our separate destructive-column case, so basic linting alone is not the distinction.
6. You can keep the migration history you already have
Section titled “6. You can keep the migration history you already have”Changing runners should not mean replaying an established database from scratch. Ptah read our Atlas-format directory and revision table, found two applied versions, and executed only the third migration. Existing invoices kept their values. A retry found nothing pending, and CE could subsequently read all three revisions.
The handover guide shows the directory-format and revision-format settings separately. Our Flyway and Liquibase examples cover different adoption boundaries. Each tests a specific history shape; rehearse your own before replacing its runner.
7. Your registry can hold the deployable artifact
Section titled “7. Your registry can hold the deployable artifact”A schema artifact does not need a vendor-hosted registry. In the Harbor example, Ptah published to a private Harbor project over verified TLS. The publisher had push permission; the operator had a separate pull credential.
We moved the production tag to newer content. The Kubernetes deployment, which pinned the old digest, stayed on its reviewed version. It changed only after we selected the new digest and approved its plan. This gives the team control over artifact storage, access, and promotion. A digest fixes content; registry permissions and your promotion process establish who may choose it.
8. The deployment controller runs in your cluster
Section titled “8. The deployment controller runs in your cluster”The Ptah operator walkthrough starts with a schema resource, exposes the proposed SQL, and waits for an approval tied to that plan. We approved the change and confirmed convergence with a fresh database observation at the current resource generation.
The controller, target credentials, and approval record stay in the Kubernetes environment you operate. The operator documentation covers that deployment model. The walkthrough records the operator source revision used for that run. Evaluate the current release and support policy for your deployment.
9. Your CI can enforce the release decision
Section titled “9. Your CI can enforce the release decision”You can also deploy without Kubernetes. Our CI walkthrough checks the starting schema, applies reviewed migrations with a scoped database role, and checks the target schema before starting a worker with separate credentials.
An unexpected starting column stopped the migration. A SQL failure rolled back its transaction. Valid SQL that produced the wrong final schema kept the new worker from starting. Those cases ran in GitHub Actions; the database and CLI checks are ordinary commands that your own runner can execute with an authorized route to the target.
This lets you keep deployment policy in the release system you already operate. The migration role need not be an application credential or a superuser. A failed final check still needs recovery review: committed SQL is not undone by skipping the application step.
10. AI can help without owning deployment
Section titled “10. AI can help without owning deployment”In one interactive Assist session, we asked for shipment tracking, then clarified that codes are unique per carrier. The model revised the proposal to use a carrier/code pair and a both-or-neither constraint. We reviewed the files and approved their write. The database changed only when we later ran the regular migration CLI.
Assist accepts a provider profile you control. For a fully self-hosted AI workflow, choose a model endpoint and client in your own infrastructure. Our recorded run used a configured endpoint; it did not establish that provider’s retention policy or model provenance.
The MCP Inspector example makes the tool boundary visible without a model: a stale preview was refused, one-time approval wrote the exact patch, and reusing its token failed. You can inspect what the assistant reads and approve what it writes while retaining the separate deployment decision.
Choose the workflow your team can operate
Section titled “Choose the workflow your team can operate”The recommendation is strongest when your team needs advanced schema and migration tooling while retaining control of its implementation, artifacts, and execution environment. Hosting those components yourself also makes you responsible for their availability, upgrades, access rules, and recovery.
Start with the Atlas handover on a disposable copy of your schema and history. Then add the checks and deployment path your team needs. The evidence map ties these reasons to the recorded runs and documentation, including their limits. It is a selection of verified workflows, not a benchmark claiming every Ptah capability matches every Atlas Pro feature.
Put Ptah to work
Example files
- application.sql
- atlas-history/20260921000100_create_invoices.sql
- atlas-history/20260921000200_add_paid.sql
- atlas-history/atlas.sum
- build-provenance.json
- evidence-map.json
- history.sql
- holder.sql
- implementation-review.json
- index-blocking/1789998965_index_customer_email.down.sql
- index-blocking/1789998965_index_customer_email.up.sql
- index-blocking/ptah.sum
- index-concurrent/1789998966_index_customer_email.down.sql
- index-concurrent/1789998966_index_customer_email.up.sql
- index-concurrent/ptah.sum
- locks.sql
- measured/after-data.txt
- measured/after-dry-history.txt
- measured/after-history.txt
- measured/application.txt
- measured/apply-index-blocking.stderr.txt
- measured/apply-index-blocking.txt
- measured/apply-index-concurrent.stderr.txt
- measured/apply-index-concurrent.txt
- measured/atlas_replay-application.txt
- measured/atlas_replay-compare.txt
- measured/atlas_replay-seed.txt
- measured/atlas-apply.txt
- measured/atlas-ce-build.txt
- measured/atlas-checksum-refused.stderr.txt
- measured/atlas-checksum-refused.txt
- measured/atlas-down-unavailable.stderr.txt
- measured/atlas-final-status.txt
- measured/atlas-inspect.txt
- measured/atlas-lint.stderr.txt
- measured/atlas-lint.txt
- measured/atlas-replay.txt
- measured/atlas-version.txt
- measured/atlas-view-diff.txt
- measured/audit-final-schema.txt
- measured/audit-revisions.txt
- measured/audit-rows.txt
- measured/before-data.txt
- measured/before-history.txt
- measured/commands.json
- measured/correct-queue.txt
- measured/drift.txt
- measured/final-compare.txt
- measured/final-data.txt
- measured/final-history.txt
- measured/hash-index-blocking.txt
- measured/hash-index-concurrent.txt
- measured/holder-blocking.txt
- measured/holder-concurrent.txt
- measured/interfaces.txt
- measured/lint-index-blocking-json.stderr.txt
- measured/lint-index-blocking-json.txt
- measured/lint-index-blocking.stderr.txt
- measured/lint-index-blocking.txt
- measured/lint-index-concurrent-json.stderr.txt
- measured/lint-index-concurrent-json.txt
- measured/lint-index-concurrent.stderr.txt
- measured/lint-index-concurrent.txt
- measured/locks-blocking.txt
- measured/locks-concurrent.txt
- measured/network-final.txt
- measured/network.txt
- measured/postgres-version.txt
- measured/ptah_replay-application.txt
- measured/ptah_replay-compare.txt
- measured/ptah_replay-seed.txt
- measured/ptah-apply.stderr.txt
- measured/ptah-apply.txt
- measured/ptah-build.txt
- measured/ptah-checksum-refused.stderr.txt
- measured/ptah-compare.txt
- measured/ptah-drift-refused.txt
- measured/ptah-dry-run.stderr.txt
- measured/ptah-dry-run.txt
- measured/ptah-hash.txt
- measured/ptah-inspect.stderr.txt
- measured/ptah-inspect.txt
- measured/ptah-lint.stderr.txt
- measured/ptah-lint.txt
- measured/ptah-replay.stderr.txt
- measured/ptah-replay.txt
- measured/ptah-retry.txt
- measured/ptah-version.txt
- measured/ptah-view-drift.txt
- measured/restore.txt
- measured/restored-view-compare.txt
- measured/result-blocking.txt
- measured/result-concurrent.txt
- measured/routes.txt
- measured/seed.txt
- measured/setup-index_blocking.txt
- measured/setup-index_concurrent.txt
- measured/view-drift.txt
- measured/view-restore.txt
- measured/writer-blocking.stderr.txt
- measured/writer-blocking.txt
- measured/writer-concurrent.txt
- measured/wrong-queue.txt
- migrations/20260921000100_create_invoices.sql
- migrations/20260921000200_add_paid.sql
- migrations/20260921000300_payment_queue.sql
- migrations/atlas.sum
- README.md
- risky/20260921000100_create_invoices.sql
- risky/20260921000200_add_paid.sql
- risky/20260921000300_drop_customer.sql
- risky/atlas.sum
- schema.sql
- seed.sql
- setup.sql
- state.sql
- verified.json
- view-drift.txt
- view-excerpt.txt
- writer.sql