# Coolify Deploy — Tooling All commands assume PowerShell from the project root. Load env first: ```powershell . .\.env.local.ps1 ``` 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 flow below (this instance's `/applications/*` API is 404 — see `references/coolify-4.1.2-notes.md`). ## UI-driven config for git build-from-source apps (this instance, v4.1.2) Because `/applications/*` is 404 here, configure git-based Docker-Compose apps through the UI with Playwright (run from the manager repo root so `require('playwright')` resolves): ```powershell . .\.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//environment//application/" $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 (API works even though app CRUD is 404) and watch it .\coolify_skill\scripts\Invoke-CoolifyApi.ps1 -Path "/deploy?uuid=" # poll GET /deployments/{deployment_uuid} for status+logs; then verify: .\deploy_skill\scripts\Test-ServiceOnline.ps1 -Fqdn https://myapp.urieljareth.org -ExpectTitle "" ``` ## 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. ### Full pipeline (one shot) ```powershell .\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 ```powershell # 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__` placeholders for Coolify to wire routes, named volumes, healthchecks). ```powershell .\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 ] ` [-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 ```powershell .\deploy_skill\scripts\Invoke-CoolifyRollback.ps1 ` -AppPath .\my-app ` -ApplicationUuid og88wk4 ` -CommitSha a1b2c3d ``` Pushes the SHA to `rollback/` (main untouched), repoints Coolify, deploys. You will be prompted for the SHA if you omit `-CommitSha`. ## GitHub API (low level) ```powershell # 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`. Useful endpoints for the deploy flow: ```powershell .\coolify_skill\scripts\Invoke-CoolifyApi.ps1 -Path "/projects" .\coolify_skill\scripts\Invoke-CoolifyApi.ps1 -Path "/servers" .\coolify_skill\scripts\Invoke-CoolifyApi.ps1 -Path "/applications" .\coolify_skill\scripts\Invoke-CoolifyApi.ps1 -Path "/applications/" .\coolify_skill\scripts\Invoke-CoolifyApi.ps1 -Path "/applications//deployments" .\coolify_skill\scripts\Invoke-CoolifyApi.ps1 -Method POST -Path "/deploy" -BodyJson (@{ uuid = "" } | ConvertTo-Json) ``` 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). ```powershell .\deploy_skill\scripts\Test-ServiceOnline.ps1 ` -Fqdn https://my-app.urieljareth.org ` [-Path /] ` [-ExpectedStatusCode 200] ` [-ExpectTitle ""] ` [-Screenshot ] ` [-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:` | | GitHub API 401 | `GITHUB_TOKEN` not loaded or expired | `. .\.env.local.ps1`; regenerate PAT at https://github.com/settings/tokens | | 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 ` | | 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://.urieljareth.org` |