Quick Start
Core Concepts in 90 Seconds
Offline-first. dpg plan and dpg diff never touch a database. They compare .dpg source files against a committed snapshot. CI pipelines can review migrations before any database is reached.
Idempotent. Applying the same plan twice produces zero SQL on the second run.
Safety-classified. Every generated statement is tagged SAFE, CAUTION, DESTRUCTIVE, or MANUAL. Destructive operations are blocked by default; they require --allow-destructive to proceed.
Additive grants. DPG only emits GRANT. It never auto-revokes. Removing a GRANTS entry emits nothing — write an explicit REVOCATIONS { } block to revoke.
No new DSL. Part 1 of every declaration is valid PostgreSQL DDL syntax with the leading CREATE verb stripped. FUNCTION bodies, VIEW queries, TABLE column lists — all are written exactly as PostgreSQL requires.
From Zero to First Migration
1. Install
git clone https://github.com/thec1oud/dpg
cd dpg
make install
Verify:
dpg --version
# dpg version v0.5.5-alpha.6 (commit: a3f7c91, built: 2026-04-27T00:00:00Z)2. Scaffold a New Project
mkdir myproject && cd myproject
dpg init
This creates:
myproject/
├── dpg.toml # root config
└── production/
├── dpg.toml # cluster config (add url here)
└── myapp/
├── dpg.toml # database config
└── schemas/
└── public/
└── tables/
Open production/dpg.toml and add your PostgreSQL connection URL:
[cluster]
url = "postgres://user:password@localhost:5432"
3. Write a Schema
Create production/myapp/schemas/public/tables/users.dpg:
TABLE users (
id BIGINT GENERATED ALWAYS AS IDENTITY,
email TEXT NOT NULL,
name TEXT NOT NULL,
CONSTRAINT pk_users PRIMARY KEY (id),
CONSTRAINT uq_users_email UNIQUE (email)
) {
INDICES { idx_users_email (email); }
GRANTS {
SELECT TO app_readonly;
SELECT, INSERT, UPDATE TO app_service;
}
ENABLE ROW LEVEL SECURITY;
}
4. Validate Offline
dpg validate
# ✓ 1 object compiled, 0 errors, 0 warnings
No database connection required. The compiler parses and lints all .dpg files.
5. Plan the Migration
dpg plan
Output:
-- DPG Migration
-- Cluster: production
-- Database: myapp
-- transactional
BEGIN;
-- source: schemas/public/tables/users.dpg:1
CREATE TABLE "public"."users" (
"id" bigint GENERATED ALWAYS AS IDENTITY,
"email" text NOT NULL,
"name" text NOT NULL,
CONSTRAINT "pk_users" PRIMARY KEY ("id"),
CONSTRAINT "uq_users_email" UNIQUE ("email")
);
-- source: schemas/public/tables/users.dpg:4
CREATE INDEX IF NOT EXISTS "idx_users_email" ON "public"."users" ("email");
GRANT SELECT ON TABLE "public"."users" TO "app_readonly";
GRANT SELECT, INSERT, UPDATE ON TABLE "public"."users" TO "app_service";
ALTER TABLE "public"."users" ENABLE ROW LEVEL SECURITY;
COMMIT;
No database connection required — the diff runs against the committed snapshot (empty on first run).
6. Apply the Migration
dpg apply
DPG runs the plan, prompts for approval, executes the SQL against the primary node, and updates .dpg/snapshots/production/myapp.json.
Use -y to skip the approval prompt in CI:
dpg apply -y
7. Verify No Drift
dpg verify
# ✓ No drift detected
dpg verify introspects the live database catalog and compares it against the snapshot. Exit code 0 means the live database matches the declared state.
8. Iterate
Edit a .dpg file and run dpg plan again. DPG emits only the delta:
-- Add name column to existing table
ALTER TABLE "public"."users" ADD COLUMN "name" text NOT NULL;
Bootstrap from an Existing Database
If you have an existing PostgreSQL database, use dpg dump to generate initial .dpg source files:
dpg dump --cluster production --database myapp
This connects to the database, reads the live catalog, and writes .dpg files and an initial snapshot. Objects that cannot be cleanly reconstructed from catalog data are emitted as commented-out stubs with a -- dpg:manual marker — review and replace those manually. From there, treat the generated files as your source of truth and iterate with dpg plan and dpg apply.
Next Steps
- Two-Part Syntax — the
{ }block model, structural scoping, merge rules - Schema Objects — every PostgreSQL object type with DPG source and generated SQL
- Lifecycle Directives — renames, deprecation, protected tables
- CLI Reference — all commands with flags