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]>
6.3 KiB
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 athttps://coolify.urieljareth.org, verified empirically with a root-team token. Re-check withGET /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, fillinput[type=email]+input[type=password], submit; savestorageStateand reuse it (seescripts/coolify-ui/coolify-login.mjs). - Import Playwright from the manager repo when the script lives elsewhere:
createRequire('<manager>/package.json')thenrequire('playwright'). - Dismiss recurring popups: buttons "Accept and Close", "Acknowledge & Disable This Popup", "Maybe next time".
- Build Pack is a
<select wire:model="buildPack">; robust locatorselect: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 parseddocker_composefield is null). Verify via/resources:docker_composemust be non-null after reload. - Env vars: Environment Variables tab → "Developer view" → fill the big textarea with
KEY=VALUElines → 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_3000in 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_composestays null →Yaml::parse(null)crash. Keep compose files clean UTF-8, no BOM. Check:head -c3 file | xxdmust NOT beef 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=trueadds--no-cache --pull(slow full rebuild); omitforceto reuse BuildKit cache and cut build time / cancellation risk.- Coolify's
/resourcesstatuscan lag reality (showedrunning:healthywhile 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)
ssh-keygen -t ed25519.POST /security/keyswithprivate_keyviaReadAllText.- Add the public key as a GitHub deploy key:
POST /repos/{owner}/{repo}/keys{title,key,read_only:true}. POST /applications/private-deploy-keywithbuild_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.