Skip to main content
Argo CD polls Git repositories every three minutes by default to detect changes. Configure webhooks to eliminate this delay and trigger immediate application refreshes when commits are pushed.

Overview

Webhooks provide event-driven updates:

Without Webhooks

  • Polls every 3 minutes
  • Up to 3 minute delay
  • Unnecessary API calls
  • Higher resource usage

With Webhooks

  • Instant notifications
  • Immediate updates
  • Event-driven architecture
  • Reduced resource usage
Webhooks are optional but recommended for production environments where fast feedback is important.

Supported Git Providers

Argo CD supports webhook notifications from:
  • GitHub (github.com and GitHub Enterprise)
  • GitLab (gitlab.com and self-hosted)
  • Bitbucket Cloud
  • Bitbucket Server (Data Center)
  • Azure DevOps
  • Gogs

Configuration Steps

Step 1: Configure Webhook in Git Provider

1

Navigate to repository settings

Go to your repository → SettingsWebhooksAdd webhook
2

Configure webhook

  • Payload URL: https://argocd.example.com/api/webhook
  • Content type: application/json (required)
  • Secret: Enter a secure random string (optional but recommended)
  • Events: Select “Just the push event”
  • Active: Checked
3

Save webhook

Click Add webhook
When creating the webhook in GitHub, the “Content type” must be set to application/json. The default value application/x-www-form-urlencoded is not supported.
GitHub Webhook Configuration

Step 2: Configure Argo CD Secret (Optional)

Configuring a webhook secret is optional but strongly recommended to prevent DDoS attacks and unauthorized webhook triggers.
1

Edit argocd-secret

kubectl edit secret argocd-secret -n argocd
2

Add webhook secret

Add the secret under stringData (no base64 encoding needed):
apiVersion: v1
kind: Secret
metadata:
  name: argocd-secret
  namespace: argocd
type: Opaque
data:
  # ... existing keys ...

stringData:
  # GitHub webhook secret
  webhook.github.secret: shhhh! it's a GitHub secret
  
  # GitLab webhook secret
  webhook.gitlab.secret: shhhh! it's a GitLab secret
  
  # Bitbucket webhook UUID
  webhook.bitbucket.uuid: your-bitbucket-uuid
  
  # Bitbucket Server webhook secret
  webhook.bitbucketserver.secret: shhhh! it's a Bitbucket server secret
  
  # Gogs webhook secret
  webhook.gogs.secret: shhhh! it's a gogs server secret
  
  # Azure DevOps basic auth
  webhook.azuredevops.username: admin
  webhook.azuredevops.password: secret-password
3

Save and exit

Changes take effect immediately (no restart required).

Webhook Secret Keys by Provider

Git ProviderSecret KeyNotes
GitHubwebhook.github.secretToken string
GitLabwebhook.gitlab.secretToken string
Bitbucket Cloudwebhook.bitbucket.uuidUUID from webhook
Bitbucket Serverwebhook.bitbucketserver.secretToken string
Gogswebhook.gogs.secretToken string
Azure DevOpswebhook.azuredevops.username
webhook.azuredevops.password
Basic auth credentials
For security, you can store webhook secrets in a separate Kubernetes Secret with the label app.kubernetes.io/part-of: argocd. Reference it using syntax: $<secret-name>:<key>

Alternative Secret Storage

Store webhook secrets in a separate secret:
apiVersion: v1
kind: Secret
metadata:
  name: webhook-secrets
  namespace: argocd
  labels:
    app.kubernetes.io/part-of: argocd
type: Opaque
stringData:
  github-secret: my-github-webhook-secret
Reference in argocd-secret:
stringData:
  webhook.github.secret: $webhook-secrets:github-secret

Bitbucket Cloud Special Configuration

Bitbucket Cloud requires additional configuration because it doesn’t include changed files in webhook payloads.

Why Special Handling?

  • Bitbucket Cloud webhooks lack changed file lists
  • Prevents Manifest Paths Annotation from working
  • Argo CD uses Bitbucket’s diffstat API as workaround

Requirements

Security requirement: Bitbucket Cloud callbacks only work with encrypted webhooks that include the X-Hook-UUID header.
1

Configure webhook UUID

Add the UUID from your Bitbucket webhook to argocd-secret:
stringData:
  webhook.bitbucket.uuid: {your-webhook-uuid}
2

Configure repository OAuth token (for private repos)

Argo CD uses the repository OAuth token to call Bitbucket API:
argocd repo add https://bitbucket.org/myorg/myrepo \
  --username myuser \
  --password <oauth-token>

How It Works

  1. Webhook received with X-Hook-UUID header
  2. UUID verified against webhook.bitbucket.uuid secret
  3. Argo CD calls Bitbucket diffstat API to get changed files
  4. For public repos: uses unauthenticated API client
  5. For private repos: uses repository OAuth token
  6. If API call fails, changed files list remains empty (graceful degradation)

Testing Webhooks

Verify Webhook Delivery

  1. Go to repository → SettingsWebhooks
  2. Click on your webhook
  3. Scroll to Recent Deliveries
  4. Click on a delivery to see request/response
  5. Green checkmark = successful delivery

Check Argo CD Logs

# Watch API server logs for webhook events
kubectl logs -n argocd -l app.kubernetes.io/name=argocd-server -f | grep webhook

# Successful webhook:
# "Received webhook request"
# "Webhook processed successfully"

# Failed webhook:
# "Webhook signature verification failed"
# "Webhook secret mismatch"

Trigger Test Event

# Make a commit and push
echo "test" >> README.md
git add README.md
git commit -m "Test webhook"
git push

# Watch application refresh
argocd app get myapp --watch

# Should see immediate refresh (not wait 3 minutes)

Webhook Security

Always Use Secrets

Configure webhook secrets to verify payload authenticity and prevent unauthorized triggers.

Limit Payload Size

Set webhook.maxPayloadSizeMB in argocd-cm ConfigMap (default: 50MB) to prevent DDoS.

Use HTTPS

Always use HTTPS for webhook endpoints to encrypt data in transit.

IP Allowlisting

Restrict webhook access to known Git provider IP ranges at firewall/ingress level.

Limiting Payload Size

apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-cm
  namespace: argocd
data:
  webhook.maxPayloadSizeMB: "25"  # Limit to 25MB

ApplicationSet Webhooks

ApplicationSets use separate webhook configuration for the Git Generator.

Troubleshooting

Cause: Webhook secret mismatch or missing.Solution:
  • Verify secret is configured in argocd-secret
  • Ensure secret matches what’s configured in Git provider
  • Check for typos or extra whitespace
kubectl get secret argocd-secret -n argocd -o jsonpath='{.data.webhook\.github\.secret}' | base64 -d
Causes:
  • Branch/tag mismatch in application
  • Application using commit SHA instead of branch
  • Manifest paths annotation doesn’t match changed files
Check:
# Verify application target revision
argocd app get myapp -o json | jq '.spec.source.targetRevision'

# Manually refresh to compare
argocd app get myapp --refresh
Issue: Webhook handler doesn’t differentiate between branch x and tag x.Impact: Push to branch x triggers refresh for apps pointing to refs/tags/x.Workaround: Use fully qualified references in applications:
spec:
  source:
    targetRevision: refs/heads/main  # Not just "main"
Solution: Increase payload size limit:
# In argocd-cm ConfigMap
webhook.maxPayloadSizeMB: "100"

Advanced Configuration

Webhook Ingress Configuration

Ensure your ingress allows webhook endpoint:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: argocd-server-ingress
  namespace: argocd
  annotations:
    nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
    nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
spec:
  rules:
  - host: argocd.example.com
    http:
      paths:
      - path: /api/webhook
        pathType: Prefix
        backend:
          service:
            name: argocd-server
            port:
              number: 443

Rate Limiting

The /api/webhook endpoint currently lacks rate limiting protection. Implement rate limiting at the ingress level.
# Nginx ingress rate limiting
annotations:
  nginx.ingress.kubernetes.io/limit-rps: "10"
  nginx.ingress.kubernetes.io/limit-connections: "5"

Build docs developers (and LLMs) love