GitLab Webhooks with ArgoCD: Instant GitOps Sync — Configuration

Introduction

One of the most common misconceptions in GitOps is assuming that changes in Git are instantly reflected in Kubernetes.

By default, this is not true.

ArgoCD relies on polling: Git → (wait ~3 min) → ArgoCD sync

This introduces delay, inefficiency, and unpredictability.

The correct production approach is: Git → GitLab Webhook → ArgoCD → immediate sync

This article explains not only how to configure GitLab webhooks, but more importantly:

  • Why they are required
  • How GitLab networking security affects them
  • How to troubleshoot real-world issues
  • What actually happens internally
  • How to validate the full GitOps loop

1. Why Webhooks Are Required

Default Behavior (Polling)

Without webhook: ArgoCD periodically checks Git (~3 minutes)


Problems with Polling

Delayed deployments  
Unnecessary API calls  
Poor scalability  
Non-deterministic timing  

With Webhook

git push → GitLab → ArgoCD → immediate sync

Key Insight

Webhook does NOT deploy anything
Webhook only triggers ArgoCD to re-evaluate Git

2. ArgoCD Webhook Endpoint

ArgoCD exposes a dedicated endpoint:

https://argocd.devops-db.internal/api/webhook

Why this endpoint exists

ArgoCD needs a way to: “be notified when Git changes”


Important

This endpoint must be:

✔ reachable from GitLab
✔ resolvable via DNS
✔ accessible via HTTP/HTTPS

3. GitLab Webhook Configuration

Navigate to:

GitLab → Repository → Settings → Webhooks

Required Fields

URL

https://argocd.devops-db.internal/api/webhook

Trigger

✔ Push events

Why only Push events?

Push = change in desired state

Optional: Secret Token

Generate any token you want; it can be a phrase or random characters.

37WbAzGIbXtXovDGaKVL6BoZWp4mmz24rhB6TBlYCr8Sts6QX5QiRTsK74HMfTI1

Why use a secret?

✔ prevents unauthorized calls
✔ validates webhook origin
✔ improves security

4. GitLab Network Security — The Real Challenge

This is where most real-world setups fail.


Problem 1 — Local Network Blocking

Error:

Requests to the local network are not allowed

Why this happens

GitLab protects against:

SSRF (Server-Side Request Forgery)

By default, GitLab blocks:

✔ 127.0.0.1
✔ 10.x.x.x
✔ 172.x.x.x
✔ internal domains (.internal, .lan)

Solution

Go to:

Admin Area → Settings → Network → Outbound Requests

Enable:

✔ Allow requests to the local network from webhooks and integrations
✔ Allow requests to the local network from system hooks

5. DNS Rebinding Protection — Hidden Blocker

Even after allowing local network access, requests may still fail.


Why?

GitLab also enforces: DNS rebinding protection


What it does

domain → resolves → internal IP → BLOCKED

Solution 1 (simple)

Disable: Enforce DNS-rebinding attack protection


Solution 2 (better)

Use allowlist:

argocd.devops-db.internal
172.21.5.241

Why allowlist is better

✔ keeps security
✔ allows only trusted targets

6. DNS Resolution — Critical Requirement

Webhook flow:

GitLab → resolves domain → calls endpoint

If DNS fails:

webhook fails
no sync

Validation

nslookup argocd.devops-db.internal
Server:		100.64.0.1
Address:	100.64.0.1#53

Name:	argocd.devops-db.internal
Address: 172.21.5.241

7. Connectivity Validation

DNS is not enough.


Test from GitLab host

curl -k https://argocd.devops-db.internal/api/webhook
Unknown webhook event

Why this is correct

✔ endpoint reachable
✔ ArgoCD responding
✔ only missing payload

8. End-to-End Flow

Once configured:

git push
   ↓
GitLab webhook
   ↓
ArgoCD receives event
   ↓
ArgoCD compares Git vs cluster
   ↓
If different → sync

9. Real Test — Canary Deployment Update

Step 1 — Modify Git

Change:

traffic:<br>  stable: 30<br>  canary: 70

Step 2 — Commit

git add .
DATE_MSG="[FEAT] - $(date '+%Y-%m-%d %H:%M:%S')"
git commit -m ${DATE_MSG}
git push origin main

Step 3 — Immediate Effect

No waiting.

Webhook triggers instantly

Step 4 — Validate in Kubernetes

kubectl describe ingress devops-api-canary -n devops-api
Name:             devops-api-canary
Labels:           <none>
Namespace:        devops-api
Address:          127.0.0.1
Ingress Class:    nginx
Default backend:  <default>
Rules:
  Host                           Path  Backends
  ----                           ----  --------
  devops-api.devops-db.internal
                                 /   devops-api-canary:80 (10.1.137.170:8080)
Annotations:                     argocd.argoproj.io/tracking-id: devops-api-canary:networking.k8s.io/Ingress:devops-api/devops-api-canary
                                 nginx.ingress.kubernetes.io/canary: true
                                 nginx.ingress.kubernetes.io/canary-weight: 70
Events:
  Type    Reason  Age                 From                      Message
  ----    ------  ----                ----                      -------
  Normal  Sync    2s (x6 over 4h11m)  nginx-ingress-controller  Scheduled for sync

Event log

Normal  Sync  nginx-ingress-controller  Scheduled for sync

What this proves

✔ Git change propagated
✔ ArgoCD applied new state
✔ Ingress updated traffic
✔ System reacted in real time

10. Final Architecture

Git
 ↓
GitLab (webhook)
 ↓
ArgoCD
 ↓
Helm
 ↓
Kubernetes
 ↓
Ingress (traffic control)

Leave a Reply

Your email address will not be published. Required fields are marked *