Skip to content

Mutual TLS (mTLS) for repo-server

Tip

Looking for general TLS setup across Argo CD components? See TLS configuration.

Argo CD supports mutual TLS (mTLS) between the argocd-server (API server), argocd-application-controller, argocd-applicationset-controller, and the argocd-repo-server. This ensures the repo-server only accepts connections from authorized clients that present a valid client certificate.

Note

The repo-server runs a gRPC endpoint with TLS by default. Enabling mTLS adds client certificate verification on top of server-side TLS.

Quickstart — default shared-cert setup

This is the simplest and recommended way to enable mTLS. A single client certificate and key are shared by all client components (argocd-server, argocd-application-controller, argocd-applicationset-controller, and argocd-notifications-controller). Argo CD auto-mounts the Secret into every relevant deployment — no manual volume or volumeMount configuration is required.

Step 1 — Create the argocd-repo-server-mtls Secret in the argocd namespace with the following keys:

apiVersion: v1
kind: Secret
metadata:
  name: argocd-repo-server-mtls
  namespace: argocd
type: Opaque
data:
  # CA used by argocd-repo-server to verify incoming client certificates (enables mTLS)
  client-ca.crt: <BASE64_CA_PEM>
  # Shared client certificate and key presented by all client components
  client.crt: <BASE64_CLIENT_CERT_PEM>
  client.key: <BASE64_CLIENT_KEY_PEM>
  # Optional: CA used by clients to verify the repo-server's own TLS certificate
  # server-ca.crt: <BASE64_SERVER_CA_PEM>

The Secret is automatically mounted at /app/config/reposerver/mtls in every relevant pod. All components default to reading their cert/key/CA files from that mount path, so mTLS is enabled automatically as soon as the Secret exists — no ConfigMap changes or flag overrides are required.

After creating the Secret, restart the affected deployments (or wait for the rollout). That is all that is needed for the default shared-cert mTLS setup.

Note

When mTLS is enabled, the repo-server automatically generates an ephemeral client certificate for its own internal health-check (liveness) self-connection. You don't need to change the readiness/liveness probes. On startup you should see a log line similar to:

Generated ephemeral health-check client certificate (CN=<value>)


Reference

The sections below document all available ConfigMap keys, environment variables, and advanced configuration options.

Enabling mTLS on repo-server

To enable mTLS on the argocd-repo-server, provide a client CA certificate. When configured, the repo-server requires all clients to present a certificate signed by this CA.

Environment variables

  • ARGOCD_REPO_SERVER_CLIENT_CA_PATH: Equivalent to --client-ca-path. Defaults to /app/config/reposerver/mtls/client-ca.crt.
  • ARGOCD_REPO_SERVER_DISABLE_TLS: Equivalent to --disable-tls.

Server certificate location (repo-server)

The repo-server's server certificate and key are read from:

  • /app/config/reposerver/tls/tls.crt
  • /app/config/reposerver/tls/tls.key

Configuring clients

The argocd-server, argocd-application-controller, and argocd-applicationset-controller must be configured with a client certificate and key to connect to a repo-server that has mTLS enabled. The certificate must be signed by the same CA configured on the repo-server.

Note

Legacy path vs. recommended path for TLS certificate validation

--repo-server-strict-tls (and --argocd-repo-server-strict-tls for the notifications controller) is the legacy path: when set, the component auto-discovers the repo-server certificate from the argocd-repo-server-tls Kubernetes secret. This flag is deprecated and may be removed in a future release.

--repo-server-ca-cert-path (and --argocd-repo-server-ca-cert-path for the notifications controller) is the recommended explicit path: you provide the path to a CA certificate file directly. This is required for mTLS setups and gives you full control over which CA is trusted. Use --repo-server-ca-cert-path in all new deployments.

Note

Client-side curve preferences are not required. During the TLS handshake, the server selects a mutually supported key exchange group from the groups advertised by the client. The default client configuration already includes the supported PQC groups, so clients do not need to explicitly configure curve preferences.

argocd-cmd-params-cm ConfigMap keys

These environment variables can also be set via the argocd-cmd-params-cm ConfigMap, which is the recommended approach for Kubernetes deployments:

argocd-repo-server

  • reposerver.client.ca.path — path to the client CA certificate file; defaults to /app/config/reposerver/mtls/client-ca.crt. The repo-server requires all gRPC clients to present a certificate signed by this CA when the file exists. mTLS is silently skipped if the file is absent.

argocd-server

  • server.repo.server.ca.cert.path — path to the CA certificate for verifying the repo server's TLS certificate
  • server.repo.server.client.cert.path — path to the client certificate for mTLS; defaults to /app/config/reposerver/mtls/client.crt
  • server.repo.server.client.cert.key.path — path to the client certificate key for mTLS; defaults to /app/config/reposerver/mtls/client.key

argocd-application-controller

  • controller.repo.server.ca.cert.path — path to the CA certificate for verifying the repo server's TLS certificate
  • controller.repo.server.client.cert.path — path to the client certificate for mTLS; defaults to /app/config/reposerver/mtls/client.crt
  • controller.repo.server.client.cert.key.path — path to the client certificate key for mTLS; defaults to /app/config/reposerver/mtls/client.key

argocd-applicationset-controller

  • applicationsetcontroller.repo.server.ca.cert.path — path to the CA certificate for verifying the repo server's TLS certificate
  • applicationsetcontroller.repo.server.client.cert.path — path to the client certificate for mTLS; defaults to /app/config/reposerver/mtls/client.crt
  • applicationsetcontroller.repo.server.client.cert.key.path — path to the client certificate key for mTLS; defaults to /app/config/reposerver/mtls/client.key

argocd-notifications-controller

  • notificationscontroller.repo.server.ca.cert.path — path to the CA certificate for verifying the repo server's TLS certificate
  • notificationscontroller.repo.server.client.cert.path — path to the client certificate for mTLS; defaults to /app/config/reposerver/mtls/client.crt
  • notificationscontroller.repo.server.client.cert.key.path — path to the client certificate key for mTLS; defaults to /app/config/reposerver/mtls/client.key

Shared vs. per-component client certificates

By default, the argocd-repo-server-mtls Secret uses a single shared key/cert pair (client.crt / client.key) that is mounted into every client component (argocd-server, argocd-application-controller, argocd-applicationset-controller, and argocd-notifications-controller). All components therefore present the same client certificate to the repo-server.

This is enough for most deployments. If you need the repo-server to distinguish which component is connecting (for example, to apply per-component authorization policies), you must issue separate certificates per component and configure each deployment to use its own cert.

Note

The repo-server only verifies that the client certificate is signed by the configured client CA. It does not enforce per-component identity by default. Per-component certs are only meaningful if you add your own authorization logic on top of mTLS.

Option A — Multiple keys in one Secret

Store all component certs in the single argocd-repo-server-mtls Secret under different key names, then customize the volume items projection in each deployment to expose only the relevant cert.

1. Create the Secret with per-component keys:

apiVersion: v1
kind: Secret
metadata:
  name: argocd-repo-server-mtls
  namespace: argocd
type: Opaque
data:
  client-ca.crt: <BASE64_CA_PEM>
  # argocd-server client cert
  server-client.crt: <BASE64_SERVER_CLIENT_CERT_PEM>
  server-client.key: <BASE64_SERVER_CLIENT_KEY_PEM>
  # argocd-application-controller client cert
  controller-client.crt: <BASE64_CONTROLLER_CLIENT_CERT_PEM>
  controller-client.key: <BASE64_CONTROLLER_CLIENT_KEY_PEM>
  # argocd-applicationset-controller client cert
  appsetcontroller-client.crt: <BASE64_APPSET_CLIENT_CERT_PEM>
  appsetcontroller-client.key: <BASE64_APPSET_CLIENT_KEY_PEM>
  # argocd-notifications-controller client cert
  notifications-client.crt: <BASE64_NOTIFICATIONS_CLIENT_CERT_PEM>
  notifications-client.key: <BASE64_NOTIFICATIONS_CLIENT_KEY_PEM>

2. Patch each deployment's volume to project only the relevant keys as client.crt / client.key:

# Example patch for argocd-server deployment
volumes:
- name: argocd-repo-server-mtls
  secret:
    secretName: argocd-repo-server-mtls
    items:
    - key: server-client.crt
      path: client.crt
    - key: server-client.key
      path: client.key
    - key: client-ca.crt
      path: client-ca.crt

Repeat with the appropriate key names for each component deployment. The mount path and ConfigMap keys remain unchanged — only the Secret items projection differs per deployment.

Option B — One Secret per component

Create a separate Secret for each component. Each Secret follows the same format as argocd-repo-server-mtls but contains only that component's cert.

1. Create per-component Secrets:

# argocd-server
apiVersion: v1
kind: Secret
metadata:
  name: argocd-repo-server-mtls-server
  namespace: argocd
type: Opaque
data:
  client.crt: <BASE64_SERVER_CLIENT_CERT_PEM>
  client.key: <BASE64_SERVER_CLIENT_KEY_PEM>
---
# argocd-application-controller
apiVersion: v1
kind: Secret
metadata:
  name: argocd-repo-server-mtls-controller
  namespace: argocd
type: Opaque
data:
  client.crt: <BASE64_CONTROLLER_CLIENT_CERT_PEM>
  client.key: <BASE64_CONTROLLER_CLIENT_KEY_PEM>
# ... repeat for argocd-applicationset-controller and argocd-notifications-controller

2. Add a separate volume and volumeMount to each deployment, pointing to its own Secret:

# Example patch for argocd-server deployment
volumes:
- name: argocd-repo-server-mtls-server
  secret:
    secretName: argocd-repo-server-mtls-server
volumeMounts:
- name: argocd-repo-server-mtls-server
  mountPath: /app/config/reposerver/mtls
  readOnly: true

Repeat with the appropriate Secret name for each component deployment. The mount path and ConfigMap keys remain unchanged.

Important

With Option B, the default argocd-repo-server-mtls volume that Argo CD auto-mounts must be removed or overridden in each patched deployment, otherwise both volumes will compete for the same mount path.

No per-component enforcement on the server side

Even if you configure separate client certificates per component (using Option A or Option B above), the repo-server does not distinguish between them. This is a fundamental characteristic of how the server-side TLS verification works, and operators should be aware of it before investing in per-component cert issuance.

The repo-server is configured with a single --client-ca-path flag, which points to one CA certificate file. Internally, the repository server loads that file into a single x509.CertPool. When a client connects, the TLS layer asks one question: "Is this certificate signed by the configured CA?" If yes, the connection is accepted. If no, it is rejected.

There is no binding between a specific component identity and a specific certificate. The server does not inspect the certificate's Subject, SAN, or any other field to determine which component is connecting. Any client that holds a certificate signed by the trusted CA will pass — regardless of whether it is argocd-server, argocd-application-controller, or any other process with access to a valid cert.

All client certificates must be signed by a CA trusted by the repo-server. The --client-ca-path flag accepts a PEM file containing one or more CA certificates. If you issue separate certs per component from different CAs, concatenate those CAs into a single bundle file and pass that bundle to --client-ca-path. The server will trust any cert signed by any CA in the bundle, but will still not distinguish which component is connecting.

Note

Per-component certificates are only meaningful if you implement your own authorization logic on top of mTLS — for example, an Envoy sidecar or a custom gRPC interceptor that inspects the peer certificate's Subject/SAN and enforces component-level access policies. Out of the box, Argo CD mTLS provides authentication (only cert-holding clients can connect) but not authorization (any authenticated client can call any RPC).

Verifying mTLS is active

After deploying:

  1. Check repo-server logs for the health-check certificate message when --client-ca-path is set:

Generated ephemeral health-check client certificate (CN=...)

  1. Attempt a connection from a pod without the client certificate — it should fail with a TLS error due to missing client authentication.
  2. Connections from argocd-server/argocd-application-controller/argocd-applicationset-controller should succeed when configured with matching client cert/key.

Troubleshooting

  • Error: --client-ca-path cannot be used when --disable-tls is enabled
  • Remove --disable-tls (or unset ARGOCD_REPO_SERVER_DISABLE_TLS) when enabling mTLS.
  • One of --repo-server-client-cert-path / --repo-server-client-cert-key-path missing
  • Provide both flags (or the corresponding environment variables) together.
  • Custom CA for repo-server server certificate
  • Provide --repo-server-ca-cert-path on clients so they can verify the repo-server's server certificate.