Git Push Deploy¶
PaaS Runtime turns every git push into a full build-and-deploy cycle — like Heroku, but on your own cluster. No CI tooling to wire up, no deploy scripts to maintain.
Workflow¶
sequenceDiagram
participant Dev as Developer
participant Forgejo
participant CP as Control Plane
participant Tekton
participant Harbor
participant K8s
Dev->>Forgejo: git push paas main
Forgejo->>CP: POST /v1/webhooks/forgejo (HMAC)
CP->>CP: verify HMAC + idempotency
CP->>CP: lookup app by git_url
CP->>Tekton: create PipelineRun
Tekton->>Harbor: docker build + push
Tekton->>CP: build complete callback
CP->>K8s: update Deployment image tag
K8s->>Dev: app live at https://app.runtime.di2amp.com
Prerequisites¶
1. Add an SSH key¶
PaaS uses SSH-authenticated git over the Forgejo cluster service. Add your public key once:
Via the dashboard (recommended):
- Open https://ma30.di2amp.com/runtime/dashboard/account
- Click + Add SSH key in the SSH Keys section
- Paste the contents of
~/.ssh/id_ed25519.puband give it a memorable name
Via the API:
TOKEN=$(curl -s -X POST https://runtime.di2amp.com/api/v1/auth/login \
-H 'Content-Type: application/json' \
-d '{"email":"you@example.com","password":"..."}' | jq -r '.data.access_token')
curl -X POST https://runtime.di2amp.com/api/v1/auth/ssh-keys \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d "{\"name\":\"laptop\",\"public_key\":\"$(cat ~/.ssh/id_ed25519.pub)\"}"
The response includes a SHA256:... fingerprint — keep it for verification.
2. Create the app¶
PaaS provisions:
- A K8s namespace + NetworkPolicy + Service
- A Forgejo repo at
https://forgejo.runtime.di2amp.com/<your-username>/my-app.git - A subdomain at
https://my-app.runtime.di2amp.com - An auto-issued TLS cert (cert-manager + Let's Encrypt)
The CLI auto-adds the paas git remote in your local clone.
First push walkthrough¶
# 1. Add the remote (auto-done by `paas apps create`, but if you cloned later)
git remote add paas https://forgejo.runtime.di2amp.com/<your-username>/my-app.git
# 2. Push
git push paas main
The webhook handler in the control plane:
- Verifies HMAC signature from Forgejo (
PAAS_WEBHOOK_SECRETshared secret) - Records the delivery in
webhook_deliveries (provider, delivery_id)for idempotency — replays return 200 fast without re-triggering a build - Looks up the app by
git_url(canonical, with.gitsuffix, or without — all three match) - Creates a Tekton
PipelineRunthat runs Paketo buildpacks + pushes to Harbor - On build success, updates the K8s
Deployment.spec.template.spec.containers[0].imageto the new tag - Argo Rollouts picks up the change and runs the canary (10% → 25% → 50% → 100%)
Watch a deploy live¶
# CLI logs (combined build + runtime)
paas logs --tail
# K8s pipeline runs
kubectl get pipelinerun -n paas-system -w
# Verify when live
curl -i https://my-app.runtime.di2amp.com/
Configure the default branch¶
The webhook handler only triggers builds on the configured branch (default main). To switch to production or release:
Via the dashboard: Settings → Git Source → Default branch → save.
Via the API:
curl -X PUT https://runtime.di2amp.com/v1/apps/<app-id>/git \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"url":"https://forgejo.runtime.di2amp.com/<user>/my-app.git","branch":"production"}'
Pushes to other branches still create preview environments — they don't replace the production deploy.
Troubleshooting¶
"no app matches repo"¶
The webhook reaches the control plane but no app's git_url matches the repo URL. Common causes:
- The app was created under a different Forgejo org/user than your push
git_urlis unset on the app — verify withpaas apps:show my-app- Trailing-slash mismatch — fixed in the lookup, but double-check via
paas logs
"webhook signature mismatch"¶
Forgejo and the control plane share PAAS_WEBHOOK_SECRET (mounted from the forgejo-webhook-secret K8s Secret). If you re-deployed Forgejo or rotated the secret, re-register the webhook.
Build fails¶
Watch the Tekton run:
Common build failures: missing Procfile, language not auto-detected (no package.json / requirements.txt / go.mod), or Paketo buildpack missing a system package.
Migration drift policy¶
When the control plane starts, sqlx verifies every applied migration's SHA-384 checksum against the _sqlx_migrations table. Never rewrite a migration file that has already been applied in any environment — doing so causes a checksum mismatch and CrashLoopBackOff at startup.
The correct doctrine:
- NEVER edit an applied migration (e.g.
024_alerts.sql) in-place. - ALWAYS create a new corrective migration (
029_fix_alerts_constraints.sql) with idempotent guards (IF NOT EXISTS,DO $$ ... IF NOT EXISTS ... END $$). - The corrective migration handles cleanup (e.g.
DELETE FROM … WHERE col IS NULL) before addingNOT NULLconstraints that would otherwise fail on existing rows.
Example from AA/06 cycle 5:
024_alerts.sqlwas rewritten post-apply → DB checksumf493f7d8…≠ file checksum → crash- Fix: restore
024_alerts.sqlto its 868-byte original + create029_fix_alerts_constraints.sql - Image
paas-control-plane:v_aa06_c5was the first clean startup after the fix