Skip to content

ArgoCD GitHub Webhook Design

Date: 2026-01-04 Status: Approved

Configure GitHub organization webhooks to trigger ArgoCD sync on push, replacing polling with event-driven updates.

  1. Faster deployments — Near-instant sync instead of 3-minute polling
  2. Reduce API calls — Eliminate polling overhead
  3. Event-driven architecture — Foundation for future integrations
GitHub (org webhook) → Cloudflare Tunnel → ArgoCD Server → Refresh matching apps
push event argocd-wh.fzymgc.net /api/webhook
ComponentChange
tf/cloudflare/variables.tfReplace windmill with argocd in webhook_services
GitHub OrganizationAdd webhook pointing to https://argocd-wh.fzymgc.net/api/webhook
ArgoCDAlready configured (webhook secret in Vault)
  • Webhook secret validates requests (HMAC signature)
  • Cloudflare Tunnel provides TLS termination
  • No direct cluster exposure
  • 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)

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/webhook passed through from GitHub request)
  • Removes stale windmill-wh.fzymgc.net

Note: Using HTTP since ArgoCD runs in insecure mode (server.insecure = true).

Location: Organization Settings → Webhooks

  1. Get webhook secret from Vault:

    Terminal window
    vault kv get -field=webhook.github.secret secret/fzymgc-house/cluster/argocd
  2. 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:
  3. Verify delivery:

    • Push to any repo
    • Check webhook delivery in GitHub (green ✓)
TestCommand/ActionExpected
DNS resolvesdig argocd-wh.fzymgc.netCNAME to cfargotunnel.com
Endpoint reachablecurl -I https://argocd-wh.fzymgc.net/api/webhook400 or 200
GitHub deliveryPush to repo, check webhook deliveriesGreen ✓, 200 response
ArgoCD refreshCheck app sync status after pushImmediate refresh
SymptomCauseFix
401/403Webhook secret mismatchVerify secret in Vault matches GitHub
404Tunnel route not configuredCheck Terraform applied
502ArgoCD unreachableCheck argocd-server pod/service
No refreshRepo URL mismatchVerify Application repo URL matches
  1. Delete GitHub org webhook
  2. Remove argocd from webhook_services (Terraform apply)
  3. ArgoCD falls back to polling (no outage)
  1. Apply Terraform (creates DNS + tunnel route)
  2. Verify endpoint is reachable
  3. Configure GitHub org webhook (manual)
  4. Test with push to selfhosted-cluster
  5. Document in docs/operations/argocd.md