Files
Proxmox-Coolify-Manager/deploy_skill/TOOLS.md
T

13 KiB

Coolify Deploy — Tooling

All commands assume PowerShell from the project root. Load env first:

. .\.env.local.ps1

Canonical catalog: docs/TOOL-INDEX.md — verified signatures, env requirements, and which scripts are broken on this instance.

Which pipelines actually work here (re-verified 2026-08-29, v4.3.14)

Through the public hostname, /applications/* still 404s — but that block is Cloudflare's edge, not Coolify's: against the origin (http://192.168.0.117:8000/api/v1, $env:COOLIFY_API_URL_ORIGIN) the full applications namespace responds 200 with the same token. State-changing endpoints are POST-only since v4.2. Script status today:

Script Status Why
Publish-ProjectToCoolify.ps1 ⚠️ untested on 4.3.14 calls New-CoolifyApplication.ps1 → point COOLIFY_API_URL at the origin
New-CoolifyApplication.ps1 ⚠️ untested on 4.3.14 POST /applications/public, PATCH /applications/{uuid} — respond via the origin
Invoke-CoolifyRollback.ps1 ⚠️ untested on 4.3.14 PATCH /applications/{uuid} — responds via the origin
New-CoolifyService.ps1 ✅ works POST /services
coolify-ui/*.mjs (Playwright) ✅ works drives the web UI
New-CoolifyAppViaDB.ps1 ⚠️ last resort direct INSERT into Coolify's DB

Working paths, in order of preference:

  1. Multi-container stack with docker-compose.coolify.yml → New-CoolifyService.ps1.
  2. Git build-from-source app → try the API against the origin (New-CoolifyApplication.ps1 with COOLIFY_API_URL pointed at the origin); if it misbehaves, fall back to the Playwright UI flow below.
  3. Last resort → New-CoolifyAppViaDB.ps1 (no validation, no rollback).

The scaffold/validate/verify scripts (Initialize-CoolifyProject.ps1, Test-PreDeployChecklist.ps1, Test-ServiceOnline.ps1, New-GitHubRepo.ps1) are unaffected and work normally.

Required env vars (in .env.local.ps1, gitignored):

  • COOLIFY_TOKEN — for Coolify API.
  • GITHUB_TOKEN — PAT for GitHub API (create repo, list). git push uses wincred, NOT this PAT.
  • (Optional) GITHUB_OWNER — override the default urieljarethbusiness-cpu.
  • COOLIFY_EMAIL / COOLIFY_PASSWORD — Coolify web UI login. Required for the UI-driven fallback flow below (see references/coolify-4.1.2-notes.md; the probable values are already in .env.local.ps1).

UI-driven config for git build-from-source apps (fallback path)

If the API-via-origin path misbehaves, configure git-based Docker-Compose apps through the UI with Playwright (run from the manager repo root so require('playwright') resolves):

. .\.env.local.ps1
$env:CF_EMAIL = $env:COOLIFY_EMAIL; $env:CF_PASS = $env:COOLIFY_PASSWORD
$scratch = "$env:TEMP\cf"; New-Item -ItemType Directory -Force $scratch | Out-Null

# 1. Log in once, save the session
node .\deploy_skill\scripts\coolify-ui\coolify-login.mjs "$scratch\state.json"

# 2. Configure an EXISTING app (find its uuid/project/env via GET /resources + /projects/{u}/environments)
$env:CF_STATE   = "$scratch\state.json"
$env:CF_APP_URL = "https://coolify.urieljareth.org/project/<proj>/environment/<env>/application/<app>"
$env:CF_BUILDPACK = "dockercompose"          # optional
$env:CF_RELOAD = "1"                          # MANDATORY after buildpack switch / repo compose change
$env:CF_ENVBULK = (Get-Content .\myapp\.env.coolify -Raw)   # KEY=VALUE lines -> "Save All Environment Variables"
$env:CF_DOMAINS = '{"web":"https://myapp.urieljareth.org"}' # pin per-service domains (else Coolify assigns random ones -> 503)
node .\deploy_skill\scripts\coolify-ui\Configure-CoolifyComposeApp.mjs

# 3. Trigger the deploy (POST since v4.2) and watch it
.\coolify_skill\scripts\Invoke-CoolifyApi.ps1 -Method POST -Path "/deploy?uuid=<app>"
# poll GET /deployments/{deployment_uuid} for status+logs; then verify:
.\deploy_skill\scripts\Test-ServiceOnline.ps1 -Fqdn https://myapp.urieljareth.org -ExpectTitle "<regex>"

Pipeline scripts

One-time setup: run npm install in the manager repo root to enable the Playwright-based operational verification (final pipeline step). Chromium binaries live at %LOCALAPPDATA%\ms-playwright. Without it, Test-ServiceOnline.ps1 falls back to curl-only.

Two pipelines: Publish-ProjectToCoolify.ps1 is for single-app Dockerfile builds (POST /applications/public). New-CoolifyService.ps1 is for multi-container Docker Compose stacks (POST /services). Use the right one for your project shape.

Since 2026-08-29 the first pipeline should also work by pointing COOLIFY_API_URL at the origin (http://192.168.0.117:8000/api/v1) — the 404 on /applications/* was Cloudflare's edge, not Coolify's. Verify on the next deploy before relying on it.

Full pipeline (one shot) — via the origin

Blocked only through the public hostname (Cloudflare 404 on /applications/*). Point the API at the origin and step 5 should succeed:

.\deploy_skill\scripts\Publish-ProjectToCoolify.ps1 `
    -AppPath .\my-app `
    -AppName my-app `
    -Fqdn https://my-app.urieljareth.org `
    -Stack node -AppPort 8080 -Init

Runs: init → checklist → create GitHub repo → push → create Coolify app → deploy → Coolify-state inspection → operational verification (HTTP 200 + Playwright browser). One confirmation per step unless -Force.

Step by step

# 1. Scaffold Dockerfile/compose/.dockerignore/.gitignore additions
.\deploy_skill\scripts\Initialize-CoolifyProject.ps1 -Path .\my-app -Stack node -AppPort 8080

# 2. Validate against AGENTS-coolify-apps.md hard rules
.\deploy_skill\scripts\Test-PreDeployChecklist.ps1 -Path .\my-app -ExpectedPort 8080 -Strict

# 3. Create GitHub repo (idempotent; aborts if it already exists)
.\deploy_skill\scripts\New-GitHubRepo.ps1 -Name my-app -Description "..." [-Private]

# 4. Push (uses wincred, not the PAT)
cd .\my-app
git init -b main
git remote add origin https://github.com/urieljarethbusiness-cpu/my-app.git
git add -A
git commit -m "Initial commit"
git push -u origin main

# 5. Create Coolify app + push envs + trigger deploy
.\deploy_skill\scripts\New-CoolifyApplication.ps1 `
    -RepoUrl https://github.com/urieljarethbusiness-cpu/my-app.git `
    -Branch main `
    -Fqdn https://my-app.urieljareth.org `
    -PortsExposes 8080 `
    -EnvFile .\.env.coolify

# 6. Verify (Coolify state: containers, proxy logs, sibling DNS)
.\deploy_skill\scripts\Test-PostDeploy.ps1 `
    -Fqdn https://my-app.urieljareth.org `
    -ContainerName my-app-main `
    -SiblingService postgres

# 7. Verify (OPERATIONAL: HTTP 200 + Playwright browser render)
.\deploy_skill\scripts\Test-ServiceOnline.ps1 `
    -Fqdn https://my-app.urieljareth.org `
    -ExpectTitle "My App" -Retries 8

Multi-container stack (Docker Compose) — New-CoolifyService.ps1

For projects that ship a docker-compose.coolify.yml (app + DB + sidecars). POSTs the compose to Coolify's /services endpoint. The compose must already satisfy the hard rules (no published 80/443, DB by service name, SERVICE_FQDN_<svc>_<port> placeholders for Coolify to wire routes, named volumes, healthchecks).

.\deploy_skill\scripts\New-CoolifyService.ps1 `
    -AppPath .\my-stack `
    -AppName my-stack `
    -ComposeFile docker-compose.coolify.yml `
    -Fqdn https://my-stack.urieljareth.org `
    -PrimaryService web `
    [-ExtraUrls @{ api = 'https://my-stack-api.urieljareth.org' }] `
    [-ProjectName default] [-EnvironmentName production] `
    [-EnvFile .\my-stack\.env.coolify] `
    [-InstantDeploy | -NoDeploy] `
    [-ServiceUuid <existing-uuid-to-update>] `
    [-Force]

-PrimaryService web maps the FQDN to the web service in the compose (via the urls payload). -ExtraUrls adds extra service→FQDN mappings for stacks with multiple public services. On success, prints service_uuid, primary_fqdn, primary_service — pipe those into Test-PostDeploy.ps1 and Test-ServiceOnline.ps1 for verification.

Rollback / redeploy from a previous commit

.\deploy_skill\scripts\Invoke-CoolifyRollback.ps1 `
    -AppPath .\my-app `
    -ApplicationUuid og88wk4 `
    -CommitSha a1b2c3d

Pushes the SHA to rollback/<sha> (main untouched), repoints Coolify, deploys. You will be prompted for the SHA if you omit -CommitSha.

GitHub API (low level)

# Who am I?
.\deploy_skill\scripts\Invoke-GitHubApi.ps1 -Path "/user"

# List my repos (paginated; first 30)
.\deploy_skill\scripts\Invoke-GitHubApi.ps1 -Path "/user/repos?per_page=30"

# Check if a repo exists
.\deploy_skill\scripts\Invoke-GitHubApi.ps1 -Path "/repos/urieljarethbusiness-cpu/my-app"

# Anything else: see https://docs.github.com/rest

Coolify API (already documented in coolify_skill)

The deploy skill reuses coolify_skill/scripts/Invoke-CoolifyApi.ps1. Endpoints that work here, useful for the deploy flow:

.\coolify_skill\scripts\Invoke-CoolifyApi.ps1 -Path "/projects"
.\coolify_skill\scripts\Invoke-CoolifyApi.ps1 -Path "/servers"
.\coolify_skill\scripts\Invoke-CoolifyApi.ps1 -Path "/services"
.\coolify_skill\scripts\Invoke-CoolifyApi.ps1 -Path "/resources"            # enumerate apps + get uuids
.\coolify_skill\scripts\Invoke-CoolifyApi.ps1 -Method POST -Path "/deploy?uuid=<uuid>"   # trigger a deploy (POST-only since v4.2)
.\coolify_skill\scripts\Invoke-CoolifyApi.ps1 -Path "/deployments/<deployment-uuid>"

⚠️ /applications* and /github-apps 404 through the public hostname (Cloudflare edge block, re-verified 2026-08-29) — call them via the origin ($env:COOLIFY_API_URL_ORIGIN = http://192.168.0.117:8000/api/v1), where the full namespace works. /resources remains the simplest inventory; /deployments/<uuid> covers deployment status.

Remember -Raw if you need objects instead of a JSON string — see docs/TOOL-INDEX.md §1.1.

For per-endpoint schemas, search coolify_skill/references/ops/ with rg.

Templates

File Use
references/templates/Dockerfile.node Node app with healthcheck on /health
references/templates/Dockerfile.python Python/gunicorn app
references/templates/docker-compose.app-db.yml App + sibling Postgres, external coolify network, named volumes
references/templates/env.local.template.ps1 Template for the operator's .env.local.ps1

Operational verification — Test-ServiceOnline.ps1

The "definition of done". Confirms the deployed service actually serves a working page (not just that Coolify reports the container as running).

.\deploy_skill\scripts\Test-ServiceOnline.ps1 `
    -Fqdn https://my-app.urieljareth.org `
    [-Path /] `
    [-ExpectedStatusCode 200] `
    [-ExpectTitle "<regex>"] `
    [-Screenshot <png-path>] `
    [-SkipBrowser] `
    [-Retries 8] [-TimeoutMs 30000]

Two layers (both must pass):

  1. HTTP (curl): status code in 2xx/3xx range. Catches Traefik 502, wrong ports_exposes, TLS DNS challenge not done, missing Cloudflare tunnel route.
  2. Browser (Playwright + Chromium): real page navigation. Catches client-side JS crashes (pageerror), failed main-frame requests, blank error pages. Saves a PNG screenshot for evidence.

Retries default to 8 (≈80s of attempts with 10s gaps) to absorb Traefik/TLS warm-up after a fresh deploy. Exits 0 on success, non-zero on failure.

-SkipBrowser runs curl-only (for CI without a display, or when Chromium is unavailable). If Node/Playwright are missing entirely, the browser layer is auto-skipped with a warning (NOT a failure) — install with npm install in the manager repo root.

Commands that always require confirmation

  • Creating a GitHub repo (New-GitHubRepo.ps1 warns if it already exists).
  • Any git push (the pipeline asks before pushing).
  • Any Coolify POST/PATCH/DELETE (project, environment, application, env, deploy).
  • Rollback (push + repoint + deploy).
  • Anything touching Cloudflare tunnel/DNS.

-Force skips the prompts. Use only after manually reviewing the plan.

Troubleshooting

Symptom Likely cause Fix
git push asks for username/password wincred cache miss Run any git HTTPS op once interactively; or cmdkey /generic:git:https://github.com /user:urieljarethbusiness-cpu /pass:<PAT>
GitHub API 401 GITHUB_TOKEN not loaded or expired . .\.env.local.ps1 (carries the wincred token since 2026-08-29); re-extract with git credential fill if it rotates
Coolify API 401 COOLIFY_TOKEN not loaded . .\.env.local.ps1
App at 502 after deploy ports_exposes mismatch (default is 3000) New-CoolifyApplication.ps1 -PortsExposes <real-port>
App can't reach DB used localhost or wrong network Use service name + external coolify network (see §2.1, §2.2 in AGENTS-coolify-apps.md)
FQDN auto-generated UUID FQDN not set before first deploy Always pass -Fqdn https://<name>.urieljareth.org