Skip to content
PtahOperator

Start a new database from a checkpoint

A fresh database runs the snapshot and what came after it, and ends up where the long way ends.

What it shows. What a checkpoint replaces, who selects it, and how the operator proves the shortcut arrived at the same place.

A replay of a verified run, not a live cluster. 8 checks held while it was recorded. Play types it out in the frame below; the transcript is the same session, already there.

sh · checkpoint-bootstrap
# Four files, and one of them is not a migration. The name is the whole contract.$ ls demo/migrations-checkpoint | grep -v down0000000001_create_shipments.up.sql0000000002_add_carrier.up.sql0000000003_snapshot.checkpoint.up.sql0000000004_add_delivered_at.up.sql # The checkpoint records where migration 2 left the column, not where migration 1 created it.$ sed -n '/CREATE TABLE/,$p' demo/migrations-checkpoint/0000000003_snapshot.checkpoint.up.sqlCREATE TABLE shipments (    id INTEGER PRIMARY KEY,    reference TEXT NOT NULL,    carrier TEXT NOT NULL); # The resource points at the whole artifact. Nothing in it names the checkpoint.$ APPLY=Always demo/bin/lab manifest shipments "$(cat demo/.lab/checkpoint-digest)" | grep -v '^ *#'apiVersion: operator.ptah.run/v1alpha1kind: PtahMigrationmetadata:  name: shipments  namespace: demospec:  target:    engine: PostgreSQL    coordinationKey: demo/storefront/primary    urlFrom:      name: demo-database      key: url  artifact:    ociRef: oci://e2e-registry.demo.svc.cluster.local:5000/migrations/demo@sha256:0035a6c8483da1344028cd273536c9e11a47174f80e3736bf7fcf9fbccd00c9d    registryAuthFrom:      name: demo-registry      mode: Environment      usernameKey: username      passwordKey: password      registryKey: registry    verificationPolicyFrom:      name: demo-migration-verification-policy      key: policy.yaml    transport:      plainHTTP: true  policy:    apply: Always    lockTimeout: 30s  interval: 1m  suspend: false  execution:    activeDeadlineSeconds: 300    failureRetryInterval: 10s    connectTimeout: 30s # Apply that resource to a database with no migration history.$ APPLY=Always demo/bin/lab manifest shipments "$(cat demo/.lab/checkpoint-digest)" | kubectl apply -f -ptahmigration.operator.ptah.run/shipments created # An empty history let the checkpoint stand in for 1 and 2, and 4 ran after it.$ kubectl ptah migration shipments -n "$NAMESPACE"Migration:      demo/shipmentsPhase:          InSyncArtifact:       oci://e2e-registry.demo.svc.cluster.local:5000/migrations/demo@sha256:0035a6c8483da1344028cd273536c9e11a47174f80e3736bf7fcf9fbccd00c9dHistory read:   <time>Current version:4Checkpoint:     3Applied:        4Pending:        0Last run:       Applied at <time>Run applied:    3, 4Run message:    2 migrations are recorded appliedArtifactVerified:True (PolicySatisfied)Ready:          True (HistoryMatched) No plan is published. # The table is what replaying all four would have built, down to the constraint.$ kubectl -n "$NAMESPACE" exec deploy/demo-psql -- psql -c "\d shipments"                         Table "public.shipments"    Column    |           Type           | Collation | Nullable | Default--------------+--------------------------+-----------+----------+--------- id           | integer                  |           | not null | reference    | text                     |           | not null | carrier      | text                     |           | not null | delivered_at | timestamp with time zone |           |          |Indexes:    "shipments_pkey" PRIMARY KEY, btree (id) # Nothing is pending. The shortcut is finished, not paused.$ kubectl -n "$NAMESPACE" get ptahmigration shipments -o json \  | jq -r '"pending: \(.status.history.pendingCount)", "current: \(.status.history.currentVersion)", "plan: \(.status.plan // "none")"'pending: 0current: 4plan: none

What was checked

Every step states what has to hold before its output may be published. A condition is read as its type, status and reason on the live object, never as a phrase in a message.

StepClaimHeld
1exits 0, prints "0000000003_snapshot.checkpoint.up.sql", prints "0000000004_add_delivered_at.up.sql"yes
2exits 0, prints "carrier TEXT NOT NULL"yes
3exits 0, prints "kind: PtahMigration", prints "ociRef: oci://", prints "apply: Always", never prints "0000000003", never prints "checkpoint"yes
4exits 0, prints "created"yes
5ptahmigration/shipments: Ready=True (HistoryMatched)yes
5exits 0, prints "InSync", prints "Current version:4", prints "Run applied: 3, 4", never prints "CREATE TABLE", never prints "ALTER TABLE"yes
6exits 0, prints "carrier", prints "delivered_at", prints "not null"yes
7exits 0, prints "pending: 0", prints "current: 4", prints "plan: none"yes

What it ran against

  • Kubernetes1.37.0
  • Operator8c7fe187707f
  • Ptahv0.6.0
  • Executorsha256:91ec0a6fa27f
  • Recorded2026-09-17
  • Scenarios at8c7fe187707f

What the commands read

A session is repeated, not admired, so every name in it is one your own environment can carry. There is nothing else: no helper of ours stands between a command and the cluster, and the recorder refuses a step that reads a name this list does not hold. The commands arekubectl, ptah and kubectl ptah, each of which you install once; the last one is the operator’s own read-only client.

  • KUBECONFIGthe cluster the commands run against
  • NAMESPACEthe namespace the schema and its database credentials live in
  • OPERATOR_NAMESPACEwhere the chart installed the operator
  • CONTROLLERthe controller Deployment’s name, from that release
  • REGISTRY_IN_CLUSTERthe registry address a Pod in the cluster resolves
  • PTAH_OCI_REGISTRYthe registry address the push goes to
  • PTAH_OCI_USERNAMEthe account that push uses
  • PTAH_OCI_PASSWORDits password, which no command prints

A session also uses demo/bin/lab in two places, and both are the lab handing over what it generated rather than doing the demonstration’s work:lab credentials prints the three registry values above, and lab manifestfills the published template with values the step states. Rundemo/acceptance/reproduce.sh to watch the same scenario repeated from a directory where neither exists, by an account that may not create a Job.

The scenario is demo/scenarios/checkpoint-bootstrap.yaml, and make demo replays the whole set against a lab of your own. All recorded runs.