Add Coolify deployment and label conventions

This commit is contained in:
2026-10-26 21:36:36 +08:00
parent 13fbe97126
commit 0279c3a591
+100
View File
@@ -477,3 +477,103 @@ or convention:
Prefer updating this skill over repeating ad-hoc instructions in a project: if a Prefer updating this skill over repeating ad-hoc instructions in a project: if a
convention applies to more than one repo, it belongs here. convention applies to more than one repo, it belongs here.
## 8. Coolify
Host: `https://coolify.tzgyn.dev`. API: `$COOLIFY_URL/api/v1`, bearer
`$COOLIFY_TOKEN` (keep the token in an env var or secret manager; never commit
it). The UI and API are the same deployment.
### Labels are the config
- Coolify stores an app's labels in `custom_labels` as **base64**. The UI calls
the field **Container Labels**; in v4.5 it is under **General → Container →
Labels** (older versions place it under General/Advanced).
- `is_container_label_readonly_enabled` (UI: **Label management**):
- `true` — “Managed by Coolify (auto-generated)”: Coolify may regenerate
labels from domains/proxy settings and **drop manual additions**.
- `false` — “Managed manually (edit labels yourself)”: `custom_labels` is
kept verbatim; domain/proxy changes no longer update it.
Set it to `false` whenever you add labels Coolify does not generate.
- API-created apps do **not** get the generated HTTP labels merged. Put the
complete set (HTTP routers, middlewares, certresolver, Caddy HTTP labels, and
any TCP routers) in `custom_labels`.
- PATCH only the field, preserving existing labels:
```bash
curl -X PATCH -H "Authorization: Bearer $COOLIFY_TOKEN" \
-H 'Content-Type: application/json' \
-d "{\"custom_labels\":\"$(base64 < labels.txt)\"}" \
"$COOLIFY_URL/api/v1/applications/$APP"
```
Decode it back and verify. Nested/double base64 can appear after API updates.
- **After every app update/recreation/deploy, check the TCP lines are still
there.** If they vanish, Traefik terminates the SNI with its default
certificate.
### HTTP vs raw TCP
- `Domains` only creates HTTP(S) routers (Traefik terminates TLS). Do not add a
wildcard domain when the proxy must pass TLS through — that would make
Traefik ask for a wildcard certificate and decrypt tenant traffic.
- For SNI passthrough (tunnels, non-HTTP services), add the full set:
```text
traefik.tcp.routers.<name>.entrypoints=https
traefik.tcp.routers.<name>.rule=HostSNIRegexp(`^.+\.<domain>$`)
traefik.tcp.routers.<name>.service=<name>
traefik.tcp.routers.<name>.tls.passthrough=true
traefik.tcp.services.<name>.loadbalancer.server.port=8443
```
Traefik dials the container port over the `coolify` Docker network; no host
port publish is needed. The app must listen on that port. The `^.+\.` regex
does not match the apex, so the API domain can stay on the HTTP router.
- Keep the HTTP labels in the same `custom_labels` value when using a manual
label set; otherwise the dashboard/API router disappears.
### API and deploys
- Deploy: `POST $COOLIFY_URL/api/v1/deploy?uuid=<app>`.
- App state: `GET /api/v1/applications/<uuid>` (`status`, `updated_at`).
- App logs: `GET /api/v1/applications/<uuid>/logs` (JSON `logs` string).
- The deployment list JSON can contain invalid escapes/HTML; `jq` sometimes
fails. Parse with Python (`json.load`, then join `logs[].output`). Prefer the
specific deployment endpoint or app status over a long poll of
`/deployments`.
- A deploy can transiently report `running:unhealthy`; wait for the app and hit
`/health` before debugging.
- Env vars: `is_runtime` and `is_buildtime`; preview and production entries
can duplicate the same key. Secrets belong in Coolify envs, not the repo.
### Gitea webhook
- Push-to-deploy uses Coolify's manual Gitea webhook:
`X-Hub-Signature-256` = HMAC-SHA256 of the raw body with the app's
`manual_webhook_secret_gitea`; Gitea sends this natively. A curl replay needs
`X-Gitea-Event: push` and the signature.
- **Gitea's hook edit API does not update the stored secret.** Delete and
recreate the hook (branch filter `main`). The app's secret is regenerated
when the app is recreated, so recreate the hook too.
- The audit log is the oracle: `webhook.deployment.queued` vs
`webhook.gitea.signature_failed`.
- Coolify pins the pushed SHA (`git fetch origin <sha>`), but this Gitea only
serves the current tip by SHA. If a later push lands before the fetch, the
deploy fails with “not our ref”: re-trigger the deploy. Permanent fix inside
the Gitea container:
```bash
git -C /data/git/repositories/TZGyn/<repo>.git config \
uploadpack.allowReachableSHA1InWant true
```
- After any app recreation: recreate the webhook and re-check custom labels.
### App creation/recreation
- Use `domains` in the API (not `fqdn`). `private_key_uuid` cannot be changed
on update; recreate the app with the key. Use storage type `persistent` for
data volumes.
- Verify after deploy: app healthy, the real TCP/SNI path (not just HTTP),
custom labels present, and the webhook queued.