Skip to content

fix: bypass Cloudflare Bot Fight Mode in trigger-coolify-deploy health check - #5

Merged
Andreas-Garcia merged 1 commit into
mainfrom
fix/health-check-bypass-cloudflare-bot-fight-mode
Aug 8, 2026
Merged

fix: bypass Cloudflare Bot Fight Mode in trigger-coolify-deploy health check#5
Andreas-Garcia merged 1 commit into
mainfrom
fix/health-check-bypass-cloudflare-bot-fight-mode

Conversation

@Andreas-Garcia

Copy link
Copy Markdown
Member

Summary

  • Add health_check_origin_ip input to trigger-coolify-deploy: when set, the health-check request uses curl --resolve <host>:443:<ip> to connect directly to the app's origin server, bypassing any reverse proxy (Cloudflare) in front of the hostname.

Motivation

Dogfooding the new deploy-api-and-web.yml workflow in the-music-deck-admin (staging push) surfaced a real bug: Deploy API (staging) hung for its full poll_timeout_seconds, then failed with Timed out after 900s waiting for https://deck-admin-api-staging.themusictree.org/health to return 200 (last: 403) — even though Coolify's own deployment record had already reached status=finished and the container was healthy (confirmed via direct SSH: clean startup log, curl .../health → 200 from the VPS itself).

Root cause: infrastructure's ansible/playbooks/group_vars/all.yml already documents this exact constraint (see comment above coolify_api_subdomain's DNS record) — Cloudflare's Bot Fight Mode issues an instant JS challenge (HTTP 403) to GitHub Actions runner IPs on any Cloudflare-proxied hostname, and it runs outside the Ruleset Engine, so no Custom Rule can exempt it. tmd-admin-api's staging/prod hostnames are intentionally proxied: true (for WAF/DDoS coverage) — that's correct and shouldn't change. The PR-preview health check worked previously only because preview subdomains are proxied: false.

Changes

  • trigger-coolify-deploy/action.yml: new optional health_check_origin_ip input. When set, the health-check curl call adds --resolve <health_host>:443:<ip>, connecting straight to the origin IP while keeping the correct SNI/Host header (so TLS/cert validation and virtual-hosting on Traefik still work normally).
  • Updated README.md / CHANGELOG.md.

Migration & Deployment notes

  • DB migration required
  • New env vars (list them)
  • Seed script must run after deploy

No new secrets. Callers pass the existing org variable vars.SERVER_HOST (207.180.249.29, already ALL-visibility) as health_check_origin_ip for staging/prod api jobs.

Test plan

  • Release as a patch/minor version once merged
  • the-music-deck-admin: bump deploy-api-and-web.yml to the new version, add health_check_origin_ip: ${{ vars.SERVER_HOST }} to the api jobs, re-run the staging deploy and confirm the health check passes instead of 403ing

…h check

Cloudflare's Bot Fight Mode issues an instant JS challenge (HTTP 403) to
GitHub Actions runner IPs on any Cloudflare-proxied hostname, and it runs
outside the Ruleset Engine so no Custom Rule can exempt it. Add
health_check_origin_ip so callers can curl --resolve the app's real origin
IP directly, skipping the proxy for just the health-check request.
Copilot AI lite review requested due to automatic review settings August 8, 2026 18:24

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an opt-in mechanism to make trigger-coolify-deploy health checks connect directly to an origin IP (via curl --resolve) so Cloudflare-proxied hostnames don’t fail CI health checks due to Bot Fight Mode.

Changes:

  • Added optional health_check_origin_ip input and wired it into the health-check curl invocation.
  • Updated README usage/docs for the new input.
  • Documented the fix in CHANGELOG.md under [Unreleased].

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
.github/actions/trigger-coolify-deploy/action.yml Adds health_check_origin_ip and applies curl --resolve during health checks.
README.md Documents the new input and shows example usage.
CHANGELOG.md Notes the new input/fix in the Unreleased section.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +223 to +230
resolve_opts=()
if [ -n "$HEALTH_CHECK_ORIGIN_IP" ]; then
health_host=$(printf '%s' "$base_url" | sed -E 's#^[a-zA-Z]+://##; s#[/:].*##')
resolve_opts=(--resolve "${health_host}:443:${HEALTH_CHECK_ORIGIN_IP}")
echo "Health-checking ${health_url} directly via ${HEALTH_CHECK_ORIGIN_IP} (bypassing any proxy in front of ${health_host}), timeout ${POLL_TIMEOUT_SECONDS}s..."
else
echo "Health-checking ${health_url} (timeout ${POLL_TIMEOUT_SECONDS}s)..."
fi
Comment thread README.md
Comment on lines 122 to +126
coolify_subdomain: ${{ vars.COOLIFY_API_SUBDOMAIN }}
domain: ${{ vars.DOMAIN_NAME }}
coolify_api_token: ${{ secrets.COOLIFY_API_TOKEN }}
health_check_path: /health
health_check_origin_ip: ${{ vars.SERVER_HOST }} # bypass Cloudflare proxy (Bot Fight Mode 403s CI runners)
@Andreas-Garcia
Andreas-Garcia merged commit 9ec35d0 into main Aug 8, 2026
1 check passed
@Andreas-Garcia
Andreas-Garcia deleted the fix/health-check-bypass-cloudflare-bot-fight-mode branch August 8, 2026 18:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants