Aprendido desplegando cotizador (Next.js+FastAPI+Postgres, Docker Compose, repo privado) en coolify.urieljareth.org (v4.1.2): - Documenta que /applications/* devuelve 404 en esta instancia y que configurar apps git build-from-source solo es posible por la UI web. - Nuevos scripts scripts/coolify-ui/: coolify-login.mjs (guarda storageState) y Configure-CoolifyComposeApp.mjs (build pack -> Docker Compose, Reload Compose File, env vars por Developer view, dominios por servicio). - references/coolify-4.1.2-notes.md: mapa de API funcional/404, gotchas (BOM UTF-8 rompe el parser YAML de Coolify; Reload Compose File obligatorio; pin de dominios por servicio o 503; no encolar deploys concurrentes; PowerShell [IO.File]::ReadAllText para payloads de llaves). - env.local.template.ps1: COOLIFY_EMAIL/COOLIFY_PASSWORD para la UI. - SKILL.md/TOOLS.md: seccion "instance reality" y flujo UI paso a paso. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
104 lines
6.3 KiB
Markdown
104 lines
6.3 KiB
Markdown
# Coolify v4.1.2 on this instance — hard realities (learned the hard way)
|
|
|
|
> Written while deploying `cotizador` (Next.js + FastAPI + Postgres, Docker Compose
|
|
> build pack, private repo). These are behaviors of the **specific** instance at
|
|
> `https://coolify.urieljareth.org`, verified empirically with a root-team token.
|
|
> Re-check with `GET /api/v1/version` — if it changed, re-verify the API surface.
|
|
|
|
## 1. The REST API surface is PARTIAL — `/applications/*` is 404
|
|
|
|
With a valid **root-team** token (`GET /teams/current` → "Root Team"):
|
|
|
|
| Endpoint | Result |
|
|
|----------|--------|
|
|
| `GET /version`, `GET /teams/current` | 200 |
|
|
| `GET /projects`, `POST /projects` | 200 / 201 |
|
|
| `GET /projects/{uuid}/environments` | 200 |
|
|
| `GET /servers` | 200 |
|
|
| `POST /security/keys` (create private key) | 201 |
|
|
| `GET /resources` | 200 — **the only reliable app/service/db inventory** |
|
|
| `GET /services`, `POST /services`, `/services/{uuid}/envs`, `/start` | work |
|
|
| `GET /deploy?uuid=<uuid>[&force=true]` | 200 — **triggers a deploy of any existing resource by uuid** |
|
|
| `GET /deployments/{deployment_uuid}` | 200 — status + logs (logs = JSON string) |
|
|
| **`/applications` (list)** | **404** |
|
|
| **`/applications/{uuid}` (get / PATCH)** | **404** |
|
|
| **`/applications/{uuid}/envs` (list/create)** | **404** |
|
|
| **`/applications/{uuid}/logs`** | **404** |
|
|
| **`POST /applications/public`** | **404** |
|
|
| **`POST /applications/dockerfile`** | **404** |
|
|
| **`POST /applications/private-deploy-key`** | **404** |
|
|
| **`POST /applications/private-github-app`** | **404** |
|
|
| **`POST /applications/dockercompose`** | **404** (deprecated upstream anyway) |
|
|
| `GET /github-apps` | 404 |
|
|
|
|
**Consequence:** you CANNOT create OR configure a **git-based application that builds
|
|
from source** (Dockerfile or Docker Compose build pack) via the API here. The skill's
|
|
`New-CoolifyApplication.ps1` (POST /applications/public) will 404 on this instance.
|
|
`POST /services` only accepts `docker_compose_raw` with **no git repo** → only good for
|
|
**prebuilt-image** stacks, not `build:`-from-source composes.
|
|
|
|
→ For git+build apps you MUST use the **web UI** (drive it with Playwright). Application
|
|
config (build pack, env vars, domains) is UI-only. Only the **deploy trigger** is API-able
|
|
(`GET /deploy?uuid=`), and reads via `/resources` + `/deployments/{uuid}`.
|
|
|
|
## 2. Inventory & status via `/resources`
|
|
`GET /resources` returns every application/service/database with `uuid`, `name`,
|
|
`status` (e.g. `running:healthy`, `exited:unhealthy`), `fqdn`, `build_pack`,
|
|
`git_repository`, `docker_compose_location`, `docker_compose_raw`, `docker_compose`
|
|
(parsed), `source_type`/`source_id`, `environment_id`, `destination`. Map an app's
|
|
`environment_id` to a project by scanning `/projects/{uuid}/environments`.
|
|
|
|
Build the UI URL as:
|
|
`https://coolify.urieljareth.org/project/{project_uuid}/environment/{env_uuid}/application/{app_uuid}`
|
|
(+ `/environment-variables`, `/logs`, `/deployment`, `/source`, `/danger`, …)
|
|
|
|
## 3. Driving the UI with Playwright (Livewire app)
|
|
- Login: `/login`, fill `input[type=email]` + `input[type=password]`, submit; save
|
|
`storageState` and reuse it (see `scripts/coolify-ui/coolify-login.mjs`).
|
|
- Import Playwright from the manager repo when the script lives elsewhere:
|
|
`createRequire('<manager>/package.json')` then `require('playwright')`.
|
|
- Dismiss recurring popups: buttons "Accept and Close", "Acknowledge & Disable This Popup",
|
|
"Maybe next time".
|
|
- **Build Pack** is a `<select wire:model="buildPack">`; robust locator
|
|
`select:has(option[value="dockercompose"])`; `selectOption('dockercompose')`.
|
|
- **After switching build pack to Docker Compose you MUST click "Reload Compose File"**
|
|
so Coolify fetches+parses+persists the compose from the repo. Otherwise the deploy
|
|
crashes: `Symfony\Component\Yaml\Yaml::parse(): Argument #1 must be of type string,
|
|
null given` (the parsed `docker_compose` field is null). Verify via `/resources`:
|
|
`docker_compose` must be non-null after reload.
|
|
- **Env vars**: Environment Variables tab → "Developer view" → fill the big textarea with
|
|
`KEY=VALUE` lines → click **"Save All Environment Variables"** (NOT a generic "Save").
|
|
Re-saving replaces the whole set, so always send the full list.
|
|
- **Pin a compose service's public domain** with a magic env var:
|
|
`SERVICE_FQDN_<SERVICE>_<PORT>=https://host` (e.g. `SERVICE_FQDN_WEB_3000=https://cotizador.urieljareth.org`).
|
|
The compose should also declare a bare `- SERVICE_FQDN_WEB_3000` in that service's env.
|
|
- **Deploy**: prefer API `GET /deploy?uuid=&force=true` (works even though app CRUD is 404),
|
|
or the UI "Deploy" button.
|
|
|
|
## 4. Deploy pitfalls seen
|
|
- **UTF-8 BOM / mojibake in the compose file breaks Coolify's YAML parser** → `docker_compose`
|
|
stays null → `Yaml::parse(null)` crash. Keep compose files clean UTF-8, **no BOM**.
|
|
Check: `head -c3 file | xxd` must NOT be `ef bb bf`.
|
|
- **Don't queue multiple deploys.** Triggering a new deploy while one is building **cancels**
|
|
the in-progress one → the build dies with `exit code 255` + "Gracefully shutting down build
|
|
container". Trigger ONE deploy and let it finish. Check `/deployments` (running list) is empty
|
|
before triggering.
|
|
- `force=true` adds `--no-cache --pull` (slow full rebuild); omit `force` to reuse BuildKit
|
|
cache and cut build time / cancellation risk.
|
|
- Coolify's `/resources` `status` can lag reality (showed `running:healthy` while Traefik
|
|
returned 503). Always confirm with an actual HTTP request to the FQDN + a browser render
|
|
(`Test-ServiceOnline.ps1`).
|
|
|
|
## 5. PowerShell 5.1 gotcha (secret payloads → API)
|
|
`Get-Content -Raw` returns a string decorated with ETS NoteProperties; `ConvertTo-Json` then
|
|
serializes it as `{"value":"..."}`, so Coolify rejects e.g. `private_key` ("must be a string").
|
|
FIX: read with `[System.IO.File]::ReadAllText($path)`.
|
|
|
|
## 6. Deploy-key path (kept as fallback / for instances where the API is complete)
|
|
1. `ssh-keygen -t ed25519`.
|
|
2. `POST /security/keys` with `private_key` via `ReadAllText`.
|
|
3. Add the public key as a GitHub deploy key: `POST /repos/{owner}/{repo}/keys` `{title,key,read_only:true}`.
|
|
4. `POST /applications/private-deploy-key` with `build_pack=dockercompose`,
|
|
`docker_compose_location`, `docker_compose_domains`, `ports_exposes`, `private_key_uuid`,
|
|
`[email protected]:owner/repo.git`. ← **404 on v4.1.2**; use the UI instead.
|