Skip to content
PtahDocs
v0.8.1
Page type: tutorial

PostgreSQL quick start

Apply and verify a first Ptah schema change against a disposable PostgreSQL, from an empty directory to a column added and checked.

The same first result as the quick start, on the engine you are likely to use. You will start a throwaway PostgreSQL, apply a desired schema, ask for a column, review the SQL that change costs, apply it, and check the database agrees. Then you remove the database and nothing is left behind.

Take this one if you already know you are on PostgreSQL. Take the SQLite quick start if you want the shortest path with no Docker and no server at all; nothing here is different about Ptah, only about what it is pointed at.

  • A ptah binary on your PATH. Install Ptah if you do not have one.
  • Docker, to run the disposable database. Nothing else is installed.
  • A terminal and about ten minutes.

Confirm that the binary runs:

Terminal window
ptah version

Step 1. Start a database you can throw away

Section titled “Step 1. Start a database you can throw away”
Terminal window
docker run -d --name ptah-quick-start \
-e POSTGRES_USER=ptah -e POSTGRES_PASSWORD=ptah -e POSTGRES_DB=app \
-p 55432:5432 postgres:18-alpine

Port 55432 rather than 5432, so this cannot collide with a PostgreSQL you already run. The password is in the command because this database exists for the next ten minutes; a database you care about takes its URL from the environment or a secret store instead.

Wait until it is ready to accept connections:

Terminal window
docker exec ptah-quick-start pg_isready -U ptah
/var/run/postgresql:5432 - accepting connections
Terminal window
mkdir ptah-postgres-quick-start
cd ptah-postgres-quick-start
cat > schema.sql <<'SQL'
CREATE TABLE users (
id BIGSERIAL PRIMARY KEY,
email TEXT NOT NULL
);
SQL
export DB='postgres://ptah:ptah@localhost:55432/app?sslmode=disable'

schema.sql is the schema you want. The database is empty.

Terminal window
ptah schema apply --schema-file schema.sql --db-url "$DB" --dry-run

Expected output on standard output:

Planned schema changes:
-- POSTGRES TABLE: users --
CREATE TABLE "users" (
"id" BIGSERIAL PRIMARY KEY NOT NULL,
"email" TEXT NOT NULL
);

--dry-run opens the database to read its current schema and executes no planned statement. The SQL is PostgreSQL’s, rendered for this engine rather than copied from the file.

Terminal window
ptah schema apply --schema-file schema.sql --db-url "$DB" --auto-approve

Expected output ends with, on standard output:

Auto-approval enabled; applying schema changes.
Schema apply completed successfully.
Terminal window
ptah schema drift --schema-file schema.sql --db-url "$DB"
No schema drift detected.

That answer comes from reading the live database, not from the file. It is the check worth putting in a pipeline.

Rewrite the schema you want. Nothing else changes:

Terminal window
cat > schema.sql <<'SQL'
CREATE TABLE users (
id BIGSERIAL PRIMARY KEY,
email TEXT NOT NULL,
created_at TIMESTAMPTZ
);
SQL

The same check now has something to report:

Terminal window
ptah schema drift --schema-file schema.sql --db-url "$DB"

Expected output includes, on standard output:

Schema drift detected (highest severity: warning).
Findings:
- columns_added: 1 (warning)

And the plan is an ALTER, against the database as it is rather than as the file describes it:

Terminal window
ptah schema apply --schema-file schema.sql --db-url "$DB" --dry-run

Expected output ends with, on standard output:

ALTER TABLE "users" ADD COLUMN "created_at" TIMESTAMPTZ;

Step 7. Apply the reviewed change, and check again

Section titled “Step 7. Apply the reviewed change, and check again”
Terminal window
ptah schema apply --schema-file schema.sql --db-url "$DB" --auto-approve
ptah schema drift --schema-file schema.sql --db-url "$DB"
No schema drift detected.

The table now carries the column, and you can see it from PostgreSQL itself:

Terminal window
docker exec ptah-quick-start psql -U ptah -d app -c '\d users'

Expected output includes, on standard output:

id | bigint | | not null | nextval('users_id_seq'::regclass)
email | text | | not null |
created_at | timestamp with time zone | | |

The database and the working directory, and nothing else:

Terminal window
docker rm -f ptah-quick-start
cd ..
rm -rf ptah-postgres-quick-start