Files
Proxmox-Coolify-Manager/deploy_skill/references/coolify-4.1.2-notes.md
T
urieljarethandClaude Opus 4.8 e7d1c33cd5 deploy_skill: soporte para Coolify v4.1.2 (API /applications 404) + flujo UI Playwright
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]>
2026-07-08 00:42:12 -06:00

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 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 parserdocker_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.