--- name: coolify-deploy description: Deploy new projects to the local self-hosted Coolify instance from end to end. Use when the user wants to take a local project (or a new idea) and ship it to Coolify: scaffold a compliant Dockerfile/compose, create a GitHub repo, push, register the app in Coolify, deploy it, and verify. Also covers rollback to a previous commit and redeploys. Distinct from coolify-agent (which operates already-deployed apps) and the proxmox-agent (host operations). --- # Coolify Deploy Skill Use this skill to take a project from "local code" to "live on `https://.urieljareth.org`". It wraps the existing repo tooling (Proxmox SSH, Coolify API, Cloudflare API) plus new GitHub scaffolding into a single end-to-end pipeline. ## ⚠️ Instance reality check (READ FIRST — verified 2026-07 on coolify.urieljareth.org, v4.1.2) This instance's REST API is **partial**: the entire `/applications/*` namespace is **404** (create public/dockerfile/private-*, list, get, PATCH, `/envs`, `/logs`). `POST /services` only takes raw compose (no git repo). Consequences: - You **cannot create OR configure a git-based build-from-source app via the API** here. `New-CoolifyApplication.ps1` (POST /applications/public) will 404. - To configure an app that **builds from a (private) git repo** — set its build pack to Docker Compose, its compose location, its **env vars**, and its **per-service domains** — you MUST drive the **web UI with Playwright**. Use `scripts/coolify-ui/*.mjs` (needs `COOLIFY_EMAIL`/`COOLIFY_PASSWORD` in `.env.local.ps1`). - What DOES work via API: `/resources` (inventory+status), `/projects`, `/security/keys`, `/services`, and **`GET /deploy?uuid=&force=true`** (trigger a deploy of any existing app), `GET /deployments/{uuid}` (status+logs). Full playbook + gotchas (UTF-8 BOM breaks Coolify's YAML parser, "Reload Compose File" is mandatory, don't queue concurrent deploys, PowerShell `ReadAllText` for key payloads, etc.): **[`references/coolify-4.1.2-notes.md`](references/coolify-4.1.2-notes.md)**. Re-verify the API surface with `GET /version` if the instance was upgraded. ## When to use - "Deploy this project to Coolify" - "Create a new GitHub repo and ship it to Coolify" - "Roll back the Coolify app to yesterday's commit" - "Generate a Dockerfile that works on this Coolify instance" - "Validate my repo against the Coolify hard rules before pushing" ## When NOT to use - Operating an already-deployed app (logs, restart, env edits) → use [`coolify_skill/SKILL.md`](../coolify_skill/SKILL.md) instead. - Proxmox host or LXC maintenance → use [`agent/SKILL.md`](../agent/SKILL.md). - Editing Cloudflare tunnel routes → dashboard-managed, see `docs/runbooks/cloudflare-tunnel.md`. ## Prerequisites (one-time setup) 1. `.env.local.ps1` must contain at minimum `COOLIFY_TOKEN` and `GITHUB_TOKEN`. See `references/templates/env.local.template.ps1`. 2. `GITHUB_TOKEN` is a GitHub PAT (classic with `repo` scope, or fine-grained with `Contents: Read+Write` + `Metadata: Read`). Generate at https://github.com/settings/tokens. 3. `git push` to GitHub uses Windows Credential Manager (`wincred`) — no need to embed the PAT in the git URL. The PAT is only for GitHub REST API calls (creating repos, listing). 4. The authenticated GitHub account is `urieljarethbusiness-cpu` (verified via `git credential fill`). Override with `GITHUB_OWNER` if needed. 5. **Operational verification needs Node + Playwright.** Run `npm install` once in the manager repo root (`H:\MegaSync\Proyectos\Proxmox & Coolify Manager`). Chromium binaries cache at `%LOCALAPPDATA%\ms-playwright`. If Node is missing, `Test-ServiceOnline.ps1` falls back to curl-only (browser layer is skipped, not failed). ## Pipeline There are **two** pipelines, one per deploy strategy. Pick based on whether your project ships as a single Dockerfile-built app or as a multi-container Docker Compose stack. ### A. Single-app (Dockerfile) — `Publish-ProjectToCoolify.ps1` ``` local project → Initialize-CoolifyProject.ps1 (optional: scaffold Dockerfile/compose) → Test-PreDeployChecklist.ps1 (gate; enforces AGENTS-coolify-apps.md) → New-GitHubRepo.ps1 (create repo, idempotent) → git push origin main (uses wincred, not the PAT) → New-CoolifyApplication.ps1 (project + env + app + envs + deploy) → Test-PostDeploy.ps1 (Coolify state: containers, proxy logs, sibling DNS) → Test-ServiceOnline.ps1 (OPERATIONAL: HTTP 200 + Playwright page render) ``` Use `Publish-ProjectToCoolify.ps1` to run all seven steps in sequence with one confirmation per step. ### B. Multi-container stack (Docker Compose) — `New-CoolifyService.ps1` For projects that ship a `docker-compose.coolify.yml` (app + sibling DB + optional sidecars). Reads the local compose file and POSTs it to Coolify's `/services` endpoint — the modern replacement for the deprecated `/applications/dockercompose`. ``` local project (with docker-compose.coolify.yml) → Test-PreDeployChecklist.ps1 (gate; enforces AGENTS-coolify-apps.md) → New-GitHubRepo.ps1 + git push (only if the repo isn't already on GitHub) → New-CoolifyService.ps1 (project + env + service + urls + envs + deploy) → Test-PostDeploy.ps1 (Coolify state: containers, proxy logs, sibling DNS) → Test-ServiceOnline.ps1 (OPERATIONAL: HTTP 200 + Playwright page render) ``` The compose MUST satisfy the hard rules (no published 80/443, DB by service name, `SERVICE_FQDN__` placeholders so Coolify wires routes, named volumes, healthchecks). The `urls` payload in `New-CoolifyService.ps1` maps one service (e.g. `web`) to the primary FQDN; pass `-ExtraUrls` for multi-public-service stacks. The final step (`Test-ServiceOnline.ps1`) is the **definition of done** for both pipelines: the service is only considered live when (a) the FQDN returns HTTP 2xx via curl and (b) a real Chromium browser loads the page with no uncaught JS errors. It retries for ~80 seconds to allow Traefik/TLS warm-up. For rollback: `Invoke-CoolifyRollback.ps1` pushes the chosen commit to a `rollback/` branch (main stays untouched), repoints the Coolify app, and redeploys. ## Hard rules (must hold for every app) Read [`docs/AGENTS-coolify-apps.md`](../docs/AGENTS-coolify-apps.md) for the canonical version. Summary: 1. **Sibling DB by service name, never `localhost`.** 2. **External `coolify` network** if the app needs shared Coolify services. 3. **Never publish host ports `80`/`443`** — Traefik owns them. 4. **FQDN = `https://.urieljareth.org`** set before first deploy. TLS via DNS challenge, not HTTP-01. 5. **Secrets as env vars only**, never committed or baked. 6. **Healthcheck on a DB-independent endpoint.** 7. **Named volumes** for data that must survive redeploys. `Test-PreDeployChecklist.ps1` enforces these automatically. ## Safety policy - **Read-only by default.** The first run of any deploy flow only *inspects*. - **Confirm before mutating.** Each step that creates a repo, pushes commits, creates a Coolify resource, or triggers a deploy asks for confirmation unless `-Force` is supplied. - **Never write secrets into the repo.** Tokens live in `.env.local.ps1` (gitignored). The skill scripts accept env files but never echo their values. - **No force-push to main** during rollback. The rollback branch carries the target SHA; main is only moved if the operator does it explicitly. - **State the rollback path** before any risky change. ## Tooling See [`TOOLS.md`](TOOLS.md) for the command reference. The scripts in `deploy_skill/scripts/` reuse: - `scripts/Invoke-ProxmoxSsh.ps1` for any LXC 102 / Docker inspection. - `coolify_skill/scripts/Invoke-CoolifyApi.ps1` for all Coolify API calls. - `scripts/Invoke-CloudflareApi.ps1` if the user asks to add a tunnel route (rare; tunnel is dashboard-managed). ## Reference docs - [`docs/AGENTS-coolify-apps.md`](../docs/AGENTS-coolify-apps.md) — the compatibility contract every project must satisfy. Read before scaffolding. - [`docs/proxmox-inventory.md`](../docs/proxmox-inventory.md) — verified topology. - [`docs/runbooks/coolify-docker.md`](../docs/runbooks/coolify-docker.md) — Docker operations inside LXC 102. - [`coolify_skill/references/ops/`](../coolify_skill/references/ops/) — per-endpoint Coolify API reference. Search with `rg`, do not load all. - [`references/templates/`](references/templates/) — Dockerfile/compose/env templates that already satisfy the hard rules. ## Common workflows ### 1. New project, full pipeline ```powershell . .\.env.local.ps1 .\deploy_skill\scripts\Publish-ProjectToCoolify.ps1 ` -AppPath .\my-new-app ` -AppName my-new-app ` -Fqdn https://my-new-app.urieljareth.org ` -Stack node -AppPort 8080 -Init ``` ### 2. Existing local repo, just register + deploy ```powershell .\deploy_skill\scripts\Publish-ProjectToCoolify.ps1 ` -AppPath .\existing-app ` -AppName existing-app ` -Fqdn https://existing-app.urieljareth.org ` -AppPort 3000 ``` ### 2b. Multi-container stack (Docker Compose) — register + deploy ```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 ` -EnvFile .\my-stack\.env.coolify ` -InstantDeploy ``` ### 3. Rollback to a previous commit ```powershell .\deploy_skill\scripts\Invoke-CoolifyRollback.ps1 ` -AppPath .\my-app ` -ApplicationUuid og88wk4 ` -CommitSha a1b2c3d ``` ### 4. Only validate, no deploy ```powershell .\deploy_skill\scripts\Test-PreDeployChecklist.ps1 -Path .\my-app -ExpectedPort 8080 -Strict ``` ### 5. Verify an already-deployed service is truly live (HTTP 200 + browser) ```powershell .\deploy_skill\scripts\Test-ServiceOnline.ps1 ` -Fqdn https://my-app.urieljareth.org ` -ExpectTitle "My App" -Retries 8 ``` This is the **operational** gate — the answer to "did the deploy actually ship a working page?". Use it after any redeploy, env change, or rollback. It captures a PNG screenshot as evidence.