Coolify DevOps

The Problem with Environment Branches

Using test/prod branches to represent environments causes merge conflict pain. The modern solution is to use main as the single source of truth and trigger environments via tags/releases + GitHub Actions calling Coolify’s deploy webhook.

Drop the test/prod branches entirely.

EnvironmentTriggerHow
DevPush to mainCoolify built-in auto-deploy webhook
TestPush a tag vX.Y.Z-rc1GitHub Action → Coolify webhook
ProdPublish a GitHub ReleaseGitHub Action → Coolify webhook

Setup

1. Coolify

Create 3 separate apps (dev/test/prod), all pointing to main. Enable auto-deploy only on the dev app.

The Two Webhooks in Coolify

Manual git webhook — this is what Coolify uses to receive push events from GitHub. It’s the same URL across all projects (Coolify routes by the shared secret). Not what you want for triggering test/prod.

Deploy webhook (auth required) — this is Coolify’s API endpoint that you call to trigger a deployment. It’s project-specific and requires a Bearer token. This is the one to use.

Setting Up the Deploy Webhook

Step 1 — Generate a Coolify API Token: In your Coolify dashboard → Security (or profile) → API Tokens → create one. This token can be reused across all your projects.

Step 2 — Get each app’s UUID: It’s embedded in the deploy webhook URL Coolify shows you on the app’s settings page:

https://your-coolify.com/api/v1/deploy?uuid=abc123xyz

Step 3 — Add GitHub Secrets per project:

SecretValue
COOLIFY_API_TOKENThe API token from Coolify (shared across projects)
COOLIFY_TEST_APP_UUIDUUID from the test app’s deploy webhook URL
COOLIFY_PROD_APP_UUIDUUID from the prod app’s deploy webhook URL

2. GitHub Action for Test

Triggers on v*-rc* tags:

on:
  push:
    tags: ['v*-rc*']
jobs:
  deploy-test:
    runs-on: ubuntu-latest
    steps:
      - name: Trigger Coolify test deployment
        run: |
          curl -X GET "https://your-coolify.com/api/v1/deploy?uuid=${{ secrets.COOLIFY_TEST_APP_UUID }}&force=false" \
            -H "Authorization: Bearer ${{ secrets.COOLIFY_API_TOKEN }}"

3. GitHub Action for Prod

Triggers on GitHub Release published:

on:
  release:
    types: [published]
jobs:
  deploy-prod:
    runs-on: ubuntu-latest
    steps:
      - name: Trigger Coolify prod deployment
        run: |
          curl -X GET "https://your-coolify.com/api/v1/deploy?uuid=${{ secrets.COOLIFY_PROD_APP_UUID }}&force=false" \
            -H "Authorization: Bearer ${{ secrets.COOLIFY_API_TOKEN }}"

Workflow in Practice

commit → main             →  dev auto-deploys ✅
git tag v1.2.0-rc1
git push --tags           →  test deploys ✅
gh release create v1.2.0  →  prod deploys ✅

No more PRs between branches, no merge conflicts. Everything flows forward from main. The tag/release is just metadata that triggers the webhook — Coolify deploys whatever commit main is at.

References