docs/operator-manual/mtls.md
[!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.
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>)
The sections below document all available ConfigMap keys, environment variables, and advanced configuration options.
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.
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.The repo-server's server certificate and key are read from:
/app/config/reposerver/tls/tls.crt/app/config/reposerver/tls/tls.keyThe 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-tlsfor the notifications controller) is the legacy path: when set, the component auto-discovers the repo-server certificate from theargocd-repo-server-tlsKubernetes secret. This flag is deprecated and may be removed in a future release.
--repo-server-ca-cert-path(and--argocd-repo-server-ca-cert-pathfor 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-pathin all new deployments.
These environment variables can also be set via the argocd-cmd-params-cm ConfigMap, which is the recommended approach for Kubernetes deployments:
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.server.repo.server.ca.cert.path — path to the CA certificate for verifying the repo server's TLS certificateserver.repo.server.client.cert.path — path to the client certificate for mTLS; defaults to /app/config/reposerver/mtls/client.crtserver.repo.server.client.cert.key.path — path to the client certificate key for mTLS; defaults to /app/config/reposerver/mtls/client.keycontroller.repo.server.ca.cert.path — path to the CA certificate for verifying the repo server's TLS certificatecontroller.repo.server.client.cert.path — path to the client certificate for mTLS; defaults to /app/config/reposerver/mtls/client.crtcontroller.repo.server.client.cert.key.path — path to the client certificate key for mTLS; defaults to /app/config/reposerver/mtls/client.keyapplicationsetcontroller.repo.server.ca.cert.path — path to the CA certificate for verifying the repo server's TLS certificateapplicationsetcontroller.repo.server.client.cert.path — path to the client certificate for mTLS; defaults to /app/config/reposerver/mtls/client.crtapplicationsetcontroller.repo.server.client.cert.key.path — path to the client certificate key for mTLS; defaults to /app/config/reposerver/mtls/client.keynotificationscontroller.repo.server.ca.cert.path — path to the CA certificate for verifying the repo server's TLS certificatenotificationscontroller.repo.server.client.cert.path — path to the client certificate for mTLS; defaults to /app/config/reposerver/mtls/client.crtnotificationscontroller.repo.server.client.cert.key.path — path to the client certificate key for mTLS; defaults to /app/config/reposerver/mtls/client.keyBy 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.
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.
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-mtlsvolume that Argo CD auto-mounts must be removed or overridden in each patched deployment, otherwise both volumes will compete for the same mount path.
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).
After deploying:
Check repo-server logs for the health-check certificate message when --client-ca-path is set:
Generated ephemeral health-check client certificate (CN=...)
Attempt a connection from a pod without the client certificate — it should fail with a TLS error due to missing client authentication.
Connections from argocd-server/argocd-application-controller/argocd-applicationset-controller should succeed when configured with matching client cert/key.
--client-ca-path cannot be used when --disable-tls is enabled
--disable-tls (or unset ARGOCD_REPO_SERVER_DISABLE_TLS) when enabling mTLS.--repo-server-client-cert-path / --repo-server-client-cert-key-path missing
--repo-server-ca-cert-path on clients so they can verify the repo-server's server certificate.