Skip to main content

db deploy

Deploy committed migrations to a remote database with dry-run verification.

Usage

postkit db deploy [--remote <name>] [--url <url>] [-f] [--dry-run]

Options

OptionDescription
--remote <name>Use specific remote (otherwise uses default)
--url <url>Direct URL override
-fSkip confirmation prompts
--dry-runVerify only, don't touch target
-v, --verboseEnable verbose output
--jsonOutput as JSON

Examples

# Uses default remote
postkit db deploy

# Use specific remote
postkit db deploy --remote staging

# Dry run to verify
postkit db deploy --remote production --dry-run

# Skip confirmations
postkit db deploy --remote staging -f

What It Does

  1. Resolves the target database URL (from remote config or --url flag)
  2. If an active session exists, removes it (with confirmation unless -f)
  3. Tests the target database connection and detects its PostgreSQL major version
  4. If localDbUrl is empty: Starts a temporary postgres:{version}-alpine container for the dry-run, version-matched to the target
  5. Applies db/infra/ (roles, schemas, extensions) to the local/temp database — before cloning, so RLS policies and grants in the target's dump (which reference custom roles) restore correctly instead of failing with role does not exist
  6. Clones the target database (full data — intentional, so the dry run tests against realistic data) to local for dry-run verification. When using a temp container, cloning runs via docker exec inside the container
  7. Runs a full dry-run on the local clone: infra (reapplied, idempotently), dbmate migrate, seeds
  8. If --dry-run is set, stops here and reports results without touching the target
  9. Reports dry-run results and confirms deployment (unless -f)
  10. Applies to target: infra, dbmate migrate, seeds
  11. Drops the local clone database; stops and removes the temp container if one was used

If the dry run fails, deployment is aborted and no changes are made to the target database.

Requirements

  • Committed migrations must exist (run db commit first)
  • localDbUrl must be different from the target remote URL (or leave it empty to use an auto-container)
  • Docker must be running if localDbUrl is empty