ArgoCD GitHub Webhook Design
Date: 2026-01-04 Status: Approved
Summary
Section titled “Summary”Configure GitHub organization webhooks to trigger ArgoCD sync on push, replacing polling with event-driven updates.
- Faster deployments — Near-instant sync instead of 3-minute polling
- Reduce API calls — Eliminate polling overhead
- Event-driven architecture — Foundation for future integrations
Architecture
Section titled “Architecture”GitHub (org webhook) → Cloudflare Tunnel → ArgoCD Server → Refresh matching apps push event argocd-wh.fzymgc.net /api/webhookComponents
Section titled “Components”| Component | Change |
|---|---|
tf/cloudflare/variables.tf | Replace windmill with argocd in webhook_services |
| GitHub Organization | Add webhook pointing to https://argocd-wh.fzymgc.net/api/webhook |
| ArgoCD | Already configured (webhook secret in Vault) |
Security
Section titled “Security”- Webhook secret validates requests (HMAC signature)
- Cloudflare Tunnel provides TLS termination
- No direct cluster exposure
Behavior
Section titled “Behavior”- On push to any fzymgc-house repo, GitHub sends webhook
- ArgoCD validates signature, checks if repo matches any Application
- If match found, triggers immediate refresh (bypasses polling)
- Non-matching repos are ignored (no-op)
Terraform Changes
Section titled “Terraform Changes”File: tf/cloudflare/variables.tf
Section titled “File: tf/cloudflare/variables.tf”Replace stale windmill default with argocd:
variable "webhook_services" { description = "Map of webhook services with their subdomain and upstream configuration" type = map(object({ service_url = string })) default = { argocd = { # No path - Cloudflare Tunnel passes through the request path as-is service_url = "http://argocd-server.argocd.svc.cluster.local" } } # ... validation unchanged}Result:
- Creates DNS:
argocd-wh.fzymgc.net→ Cloudflare Tunnel - Creates tunnel ingress: routes to ArgoCD server (path
/api/webhookpassed through from GitHub request) - Removes stale
windmill-wh.fzymgc.net
Note: Using HTTP since ArgoCD runs in insecure mode (server.insecure = true).
GitHub Webhook Configuration (Manual)
Section titled “GitHub Webhook Configuration (Manual)”Location: Organization Settings → Webhooks
Setup Steps
Section titled “Setup Steps”-
Get webhook secret from Vault:
Terminal window vault kv get -field=webhook.github.secret secret/fzymgc-house/cluster/argocd -
Configure GitHub org webhook:
- Go to:
https://github.com/organizations/fzymgc-house/settings/hooks - Click “Add webhook”
- Payload URL:
https://argocd-wh.fzymgc.net/api/webhook - Content type:
application/json - Secret: (paste from step 1)
- Events: “Just the push event”
- Active: ✅
- Go to:
-
Verify delivery:
- Push to any repo
- Check webhook delivery in GitHub (green ✓)
Verification
Section titled “Verification”| Test | Command/Action | Expected |
|---|---|---|
| DNS resolves | dig argocd-wh.fzymgc.net | CNAME to cfargotunnel.com |
| Endpoint reachable | curl -I https://argocd-wh.fzymgc.net/api/webhook | 400 or 200 |
| GitHub delivery | Push to repo, check webhook deliveries | Green ✓, 200 response |
| ArgoCD refresh | Check app sync status after push | Immediate refresh |
Troubleshooting
Section titled “Troubleshooting”| Symptom | Cause | Fix |
|---|---|---|
| 401/403 | Webhook secret mismatch | Verify secret in Vault matches GitHub |
| 404 | Tunnel route not configured | Check Terraform applied |
| 502 | ArgoCD unreachable | Check argocd-server pod/service |
| No refresh | Repo URL mismatch | Verify Application repo URL matches |
Rollback
Section titled “Rollback”- Delete GitHub org webhook
- Remove
argocdfromwebhook_services(Terraform apply) - ArgoCD falls back to polling (no outage)
Execution Order
Section titled “Execution Order”- Apply Terraform (creates DNS + tunnel route)
- Verify endpoint is reachable
- Configure GitHub org webhook (manual)
- Test with push to selfhosted-cluster
- Document in
docs/operations/argocd.md