Securing Argo CD Git webhooks

Argo CD's webhook endpoint is public and accepts payloads from Git hosts, so it needs per-provider shared secrets, careful Secret storage and a clear view of what it can and cannot trigger.

On this page

Why Argo CD webhook security matters before you expose the endpoint

A platform team that wants Git pushes to trigger faster refreshes has to expose the /api/webhook endpoint to the internet, and that endpoint must be protected with shared secrets per provider and must treat every payload as untrusted input. Git hosts call this endpoint from outside your network, so anyone who can reach it can send requests that look like pushes. The control that matters most is verifying that each request was signed with the secret you configured for that provider before Argo CD acts on anything in it.

Argo CD's security documentation states that webhook payloads are treated as untrusted and are used only to identify which applications to refresh. That design limits the damage from a forged or malformed payload, because the content does not directly change what gets deployed. Argo CD still refreshes applications when the payload points at them, so a flood of requests can keep the repo-server busy. Teams should decide before go-live which Git providers will call the endpoint, who owns each shared secret, and how a rotated secret reaches both the Git host and the cluster. Confirm the exact verification behavior for each provider in the Argo CD webhook configuration guide against the Argo CD version you run.

You need this control whenever the endpoint is reachable from outside your private network, which is the normal case for hosted Git providers that push events to a public address. If you only poll Git on an interval, or your Git host sits on the same private network as the cluster and you have verified that path, the exposure is smaller, but you should still document the decision. Review the endpoint's reachability as part of each deployment change, because a new ingress rule or load balancer can expose it without anyone intending to.

Webhook configuration options and when each fits

This table compares three ways to store the shared secret that Git providers send with webhook calls to the Argo CD endpoint. Read each row as a trade-off between simplicity and how easily the secret can be rotated or scoped. The main takeaway is that a separate labeled Secret, referenced by name and key, is usually the easiest option to rotate without touching the main Argo CD settings.

OptionWhere the secret livesWhen it fitsWhat it makes harder
One shared secret per Git providerA single value used for every repository from that providerSmall installations with one Git host and few repositories, where one owner manages all webhooksLimiting exposure: a leaked value affects every webhook from that provider, and rotation must update every repository at once
Separate Secret labeled app.kubernetes.io/part-of: argocdA dedicated Secret referenced as $<secret-name>:<key> in the webhook configurationTeams that want the secret managed on its own lifecycle, with a clear label for audit and cleanup queriesKeeping the label and key names consistent across environments, and confirming the reference syntax for your Argo CD version
Inline value in argocd-secretThe value is stored directly in the shared argocd-secret SecretQuick proofs of concept where the operator already controls every change to that SecretSeparating duties, since anyone who can edit argocd-secret also touches admin credentials and signing keys, and rotation is coupled to other changes
Per-provider secret with a separate Secret per Git hostOne labeled Secret for each Git provider, with its own keyMulti-host environments such as a self-hosted GitLab alongside a cloud Git serviceKeeping the mapping between providers, repositories and Secrets accurate over time
Rotation procedureUpdate the stored value, then the matching value on the Git hostChoose the option that lets you change one value without a full Argo CD restart or manual edits to unrelated settingsConfirm with your Argo CD version whether a restart or refresh is needed after a Secret change
Git host support for the secretCheck how your provider signs or sends the secret in each webhookChoose the option that matches what your host supports, such as GitHub, GitLab, Bitbucket or GogsConfirm the exact header and signature behavior for your Git host and version before you rely on it
Access and auditWho can read, edit or list the Secret in the argocd namespaceOptions that keep the secret in its own Secret make access review and audit queries simplerConfirm RBAC on the namespace, because read access to a Secret exposes the value to anyone with that permission

How to configure a secured webhook endpoint

This sequence takes the public Argo CD webhook endpoint from default to protected: each Git host gets its own secret, the secrets live in labeled Kubernetes Secrets, and a test push confirms that only the intended applications refresh. The result is a webhook path that treats every payload as untrusted and can be reviewed by security and platform teams.

  1. Confirm which Git hosts send webhooks

    The platform team lists every Git host and repository group that will notify Argo CD, along with the application set each one should refresh. Security and the application owners approve the list before any secret is created, because each entry becomes a separate trust relationship. Done means a written inventory exists with one row per provider, the repositories it covers, and the Argo CD applications that depend on those repositories. Confirm the exact webhook options each provider supports with that provider's own documentation, since the payload signing method differs between hosts.

  2. Create one secret per provider

    The platform engineer creates a separate Kubernetes Secret for each Git host, with a generated value of adequate length stored under a dedicated key. Each Secret carries the label app.kubernetes.io/part-of: argocd so Argo CD can read it, and it lives in the namespace where Argo CD runs. Separate secrets mean a leaked value can be rotated for one provider without breaking the others. The check is a query such as kubectl get secret with the label selector returning exactly the expected Secrets, and the values are not printed in logs or pasted into tickets.

  3. Reference the secrets in the webhook configuration

    The platform engineer points the Argo CD webhook configuration at each provider secret using the $<secret-name>:<key> syntax, so the shared value is never written into a ConfigMap or Git repository. Changes to the Argo CD configuration are made through the same review path as other platform changes. The check is that the configuration contains only references, and that the Argo CD component restarts or reloads cleanly with no errors about a missing secret or key. Confirm the reload behavior for your Argo CD version in the webhook documentation.

  4. Register the webhook URL and secret on the Git host

    The repository owner adds a webhook on each Git host pointing to the Argo CD webhook endpoint, using the same secret value that was stored in the matching Secret. Only the repositories in the approved inventory are configured, and the host is set to send only the event types Argo CD needs, such as push events. Done means each repository shows a webhook with recent delivery records and no failed deliveries. The secret value is entered once, through a secure channel, and is not copied into chat or documentation.

  5. Test with a correctly signed push

    The team makes a harmless commit to a test branch in an approved repository and confirms that only the application linked to that repository refreshes. Argo CD logs and the application view should show one refresh event for the intended application, with no sync performed by the webhook itself. This proves the payload is used only to pick which applications to refresh. Sync policy still determines what changes on the cluster, so the check should also confirm that auto-sync settings match what the owners expect.

  6. Test rejection of unsigned and wrongly signed payloads

    The security engineer sends test requests to the webhook endpoint from a machine outside the Git host, first with no signature and then with a signature made using the wrong secret. Argo CD should reject both, and the logs should record the rejection without echoing the secret or the payload content. A correctly signed request from the same source must still be accepted. Record the results with the test date and the Argo CD version so the evidence can be reused after upgrades.

  7. Confirm payload limits and document operations

    The platform team sets or confirms the payload size limit that Argo CD applies to webhook requests, then tests a request above that limit to confirm it is refused. Because the size limit does not replace rate limiting, the team records whether a rate limit exists at the ingress or gateway layer, and if not, lists that gap for the owners to decide on. Finally, the team documents the secret rotation procedure, the owner of each secret, and the steps to disable a provider's webhook. Done means a runbook exists and the open gaps have named owners.

Security controls around the webhook endpoint

The /api/webhook endpoint is the one part of Argo CD that accepts unauthenticated traffic from the internet, so its controls matter more than their size suggests. The cards below cover what each control protects, where it lives in your platform, and what to confirm before you expose the endpoint.

Per-provider shared secrets

Each Git provider signs its deliveries with a secret you configure, so use a separate secret for each provider or Git host rather than one value shared everywhere. Use a long random value generated outside Git. Plan rotation as a routine task: update the secret in Argo CD and in the provider's webhook settings in one change window, then confirm that deliveries are accepted again. Rotating one provider should not force a change to the others.

Labeled Secrets and read access

Store webhook secrets in a dedicated Kubernetes Secret labeled app.kubernetes.io/part-of: argocd, and reference the values from the Argo CD configuration with the $<secret-name>:<key> form. Keeping them out of the main configuration makes them easier to find, back up and exclude from general edits. Limit who can read that Secret through Kubernetes RBAC, because anyone who can read it can forge valid deliveries. Check which service accounts and operators in the namespace can get or list Secrets.

Treat payloads as untrusted input

Argo CD uses webhook payloads only to decide which applications to refresh, and it does not take commit content or instructions from them. Keep it that way in your design. Do not build automation that trusts payload fields for approvals, branch selection or deployment targets, and do not route payloads into other tools without validation. The sync itself still reads the repository through the normal source configuration, so review those settings separately.

Payload size limits

Cap the size of accepted request bodies at the ingress or proxy in front of Argo CD so oversized deliveries are refused before they reach the server. Choose a limit that fits your largest legitimate push payloads, and test it with a real provider delivery from a large repository. A size limit reduces memory pressure from a single request, but it does not limit how many requests arrive. Treat it as one control among several.

Rate limiting at the edge

Payload size limits do not replace rate limiting, so decide where request rates are limited before you publish the endpoint. The usual places are the ingress controller, a web application firewall or the cloud load balancer. Confirm with your platform team which layer owns the rule, what threshold applies to the webhook path, and how legitimate bursts from a busy monorepo are handled. Record the decision so the rule is not removed during an ingress change.

Rejected delivery monitoring

Rejected deliveries are the earliest signal of forged or misconfigured requests, so send Argo CD server logs and ingress access logs for the webhook path to your central logging. Alert on sustained rejections from one source, and on a sudden drop in accepted deliveries, which can mean a rotated secret was not applied. Confirm that log retention meets your audit requirements and that logs do not record the secret itself.

Limits to confirm in your environment

A webhook endpoint is the one place where Argo CD accepts unauthenticated internet traffic, so its safety depends on several layers outside Argo CD itself. The cards below list the assumptions to check with your Git host, your ingress team and your Argo CD version owner before you expose the endpoint.

Signature scheme support by Git host

Argo CD validates webhook payloads with a shared secret, but the exact signature header and algorithm depend on the provider that sends the event. Confirm with your Git host's webhook documentation which header it sends and how it signs the body, and check that your host version still sends it. Test with a delivery from a non-production repository, since a mismatched scheme usually fails silently with rejected deliveries.

Payload size and rate limits at ingress

Argo CD does not rate limit webhook requests, so the limits must come from the load balancer, ingress controller or WAF in front of /api/webhook. Ask the network team which maximum body size is enforced, what happens to oversized requests, and whether any per-source request limit exists. Treat the answers as unknown until someone has tested them against a large payload.

Version-specific webhook behavior

Webhook handling and the surrounding RBAC and secret reference behavior have changed across major releases, including the Argo CD 3.0 changes to how repositories and claims are configured. Confirm the behavior of each minor version you run in the upgrade notes and release notes, and retest webhook delivery after every upgrade and patch. Patch releases in the supported window are the ones you should expect to run.

Whether webhooks are the only public entry

Webhook security assumes that the endpoint is the only Argo CD path reachable from outside your network. Map every ingress route, load balancer listener and DNS record that points to Argo CD, and confirm that the UI and API are not published alongside the webhook path. If the API is reachable publicly, the webhook secret is no longer the main protection for the installation.

Who owns the shared secret lifecycle

The webhook secret must match on the Git host and in the Kubernetes Secret that Argo CD reads, so a rotation touches two teams. Confirm who can read the Secret, who can change the value on the Git host, and how a rotation is coordinated without dropping deliveries. Ask your security team whether this rotation fits your existing secret policy.

Questions teams ask before securing Argo CD webhooks

Can a Git webhook deploy changes to our clusters by itself?

No. Argo CD treats webhook payloads as untrusted and uses them only to identify which applications to refresh. A webhook prompts Argo CD to check Git sooner, but the content of the payload is not applied to the cluster. Changes still reach the cluster only through the sync behavior you configured, so the real exposure is an attacker forcing extra refresh cycles or sending oversized requests, which is why the endpoint still needs a shared secret and size limits. Confirm the exact behavior for your Argo CD version in the Argo CD security documentation.

How many webhook secrets do we need?

Plan for one shared secret per Git provider or per webhook configuration, not one for the whole Argo CD instance. If you run GitHub, GitLab and a self-hosted Bitbucket, that is three separate secrets, each stored in a Secret labeled app.kubernetes.io/part-of: argocd and referenced with $<secret-name>:<key>. Separate secrets let you rotate one provider without touching the others and limit the blast radius if one value leaks. The count grows with the number of Git hosts and webhook endpoints you expose, so inventory them before you start.

What does rotating a webhook secret involve?

Rotation has two sides that must change together: the value stored in the Argo CD Secret and the value configured in each Git host's webhook settings. Plan a short window where both are updated, or the webhook will fail signature checks and refreshes will stop until both sides match. Record which Secret and key each provider uses, so the person rotating knows exactly what to change. Rotation is recurring work, so decide who owns it, how often it happens under your policy, and how it appears in your change records.

Can our own team configure webhook security without outside help?

Often yes, if your engineers already run Argo CD and understand Kubernetes Secrets, ingress and Git host administration. The work is not conceptually large, but it touches three systems: the Argo CD configuration, the Git host webhook settings and the network path into the endpoint. Teams without someone who owns all three tend to miss a step, such as a stale secret or an ingress rule that blocks the provider's IP ranges. An outside partner is most useful when the setup must be documented, tested and handed over under a fixed scope.

How long does webhook hardening take, and what changes the answer?

Duration depends on scope, not on a fixed template. The main drivers are the number of Git hosts and webhook endpoints, whether the ingress path for /api/webhook already exists and is well understood, whether a web application firewall sits in front of Argo CD and must be tuned to allow provider traffic, and how many teams must agree on secret ownership. Each additional provider adds configuration, testing and documentation. Ask any provider for a written scope and timeline that names these inputs, since no duration is set until the scope is agreed.

Does a web application firewall replace the webhook shared secret?

No. A web application firewall can limit request size, block obviously malformed traffic and reduce exposure, but it cannot verify that a payload came from your Git provider unless the shared secret is checked. Keep the secret in place, then decide whether the firewall adds value on top. If you have an existing WAF, it may need rules that allow provider traffic while still enforcing payload limits, and those rules should be tested against real webhook deliveries before you rely on them.

What should we check after the webhook is configured?

Check four things: that a delivery with the correct secret triggers a refresh for the expected application, that a delivery with a wrong or missing secret is rejected, that oversized payloads are refused by your size limit, and that the Argo CD logs show the rejections without exposing secret values. Repeat these checks after each Argo CD upgrade and each secret rotation. Keep rate limiting in mind as a separate control, since the payload size limit alone does not stop a flood of valid-looking requests.

Keeping webhook secrets and endpoint rules current

Rotate webhook secrets on both sides in the same change window. The shared secret has to match in the Git host's webhook settings and in the Secret labeled app.kubernetes.io/part-of: argocd that Argo CD references through $<secret-name>:<key>. Rotate the value at the Git host, update the Secret, and confirm a test delivery is accepted before you retire the old value. Set a review interval for each provider's secret, record who owns it, and treat any engineer who has left the team or any leaked CI log as a reason to rotate immediately. Ask your Git host which settings change the secret and whether old deliveries signed with the previous value are still accepted during the switch, because that behavior varies by provider and you should confirm it in their documentation rather than assume it.

Review who can read Secrets in the argocd namespace. Anyone with get or list access to Secrets there can read the webhook values, along with repository and cluster credentials stored in the same namespace. Audit the Kubernetes RoleBindings and ClusterRoleBindings that touch that namespace each quarter, remove wildcard verbs, and confirm that Argo CD RBAC policies do not grant access to the webhook Secret through application or project permissions. Separately, check the Argo CD logs for rejected deliveries at a set cadence. Repeated signature failures from one provider may indicate a misconfigured secret after a rotation, or someone probing the public endpoint. Keep the log retention long enough that you can compare a rejection with the rotation date.

Retest the endpoint after every Argo CD upgrade and Git host change. Upgrade notes can change how Argo CD handles RBAC, resource tracking and Secret-backed configuration, so a webhook that worked before an upgrade should be checked again with a signed test delivery and a confirmation that the expected application refreshes. Revisit the whole design when you move to a different Git host, change the webhook path or the Argo CD minor version crosses a boundary covered by the upgrade notes, such as the 2.14 to 3.0 changes. Confirm the payload size limit and ingress rules still apply to the new version and that the endpoint remains untrusted input, since Argo CD uses payload content only to decide which applications to refresh.

How DeployOpen works on this

DeployOpen scopes Argo CD webhook hardening as a written, fixed-scope engagement inside your own infrastructure. We agree which webhook providers, endpoints and Argo CD installations are in scope, then build and test the secret handling, payload controls and verification before handover.

How we scope

  • Inventory every Git provider that sends webhooks to your Argo CD /api/webhook endpoint and record each one.
  • Confirm which Argo CD installations, namespaces and repositories are covered by the written scope before work starts.
  • Agree in writing who owns each shared secret, where it is stored and how rotation will be handled.

How we build

  • Configure one shared secret per provider in a separate Secret labeled app.kubernetes.io/part-of: argocd.
  • Reference each secret with the $<secret-name>:<key> pattern so values stay out of the main Argo CD configuration.
  • Set and test payload size limits, and document where rate limiting must come from your ingress or gateway layer.

How we hand over

  • Document the webhook configuration, secret names, labels and the provider-side settings your team must maintain.
  • Walk your engineers through the test cases that confirm forged or malformed payloads do not trigger refreshes.
  • Record the operating runbook for secret rotation, provider changes and verifying webhook behavior after upgrades.

How we support

  • Ongoing support, upgrades and continuous testing are available as a retainer after handover.
  • Ask for a quote built from the written scope, including what is one-time setup and what recurs under a retainer.
  • Confirm with your team how webhook changes are reviewed when Argo CD versions move forward.

Sources and further reading

Talk to our team.

Tell us what you're working on, whether it's a deployment, an audit, a security test or a cyber range. You'll speak with an engineer who can help you scope it.

  • 30-minute call: free, with no obligation.
  • NDA on request: we can sign before you share details.
  • Clear next steps: a scope and plan after the call.