Argo CD doesn’t natively inject Vault secrets; it treats them as just another Kubernetes Secret resource that your application then consumes.
Let’s see how this plays out. Imagine you have a Vault secret at secret/my-app/creds with a username and password key. You’ve configured Argo CD to sync your application, and within that application’s manifest, you have a Kubernetes Secret object defined.
apiVersion: v1
kind: Secret
metadata:
name: my-app-creds
namespace: default
type: Opaque
data:
# This is where the magic (or lack thereof) happens
# We'll fill this in later via Vault
And your application deployment looks like this:
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-app-container
image: nginx:latest
ports:
- containerPort: 80
env:
- name: DB_USERNAME
valueFrom:
secretKeyRef:
name: my-app-creds # References the K8s Secret
key: username
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: my-app-creds # References the K8s Secret
key: password
Argo CD’s job is to make sure the Deployment and the Secret exist in your cluster as defined in your Git repository. It doesn’t reach into Vault itself.
To bridge this gap, you need a component that can read from Vault and populate the Kubernetes Secret object. The most common and idiomatic way to do this is with the Vault Agent Injector (or the older Vault Agent sidecar pattern, though injection is preferred).
Here’s the mental model:
- Vault Server: Your secrets live here.
- Vault Agent Injector (running in Kubernetes): This is a Kubernetes Mutating Admission Webhook. When you create or update a Pod, it intercepts the request.
- Kubernetes API Server: The Injector talks to this.
- Argo CD: Deploys your application manifests, including the Pod/Deployment spec.
- Your Application Pod: The Pod, after being injected by the Vault Agent, will have a sidecar container (or the main container will be modified) that mounts a volume containing the rendered secrets.
How the Vault Agent Injector works:
You annotate your Deployment (or Pod template) to tell the Vault Agent Injector that it should intervene.
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
namespace: default
annotations:
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/role: "my-app-role" # K8s auth role for Vault
vault.hashicorp.com/agent-inject-secret-creds.txt: "secret/my-app/creds" # Path and filename for the secret
# vault.hashicorp.com/agent-inject-template-creds.txt: | # Optional: for templating
# {{- with secret "secret/my-app/creds" -}}
# username: {{ .Data.data.username }}
# password: {{ .Data.data.password }}
# {{- end -}}
spec:
replicas: 1
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-app-container
image: nginx:latest
ports:
- containerPort: 80
# Env vars still point to the K8s Secret, but the K8s Secret
# will be populated by the init container from Vault.
env:
- name: DB_USERNAME
valueFrom:
secretKeyRef:
name: my-app-creds # This K8s Secret will be populated by the init container
key: username
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: my-app-creds
key: password
When Argo CD deploys this Deployment, the Kubernetes API server receives the Pod definition. The Vault Agent Injector webhook intercepts it.
- It sees the
vault.hashicorp.com/agent-inject: "true"annotation. - It consults
vault.hashicorp.com/roleto authenticate with Vault using Kubernetes Service Account authentication. - It reads
vault.hashicorp.com/agent-inject-secret-creds.txtto know that it needs to fetchsecret/my-app/credsfrom Vault. - It generates a Kubernetes
Secretobject namedmy-app-creds(derived fromcreds.txt) and populates it with the data from Vault. This KubernetesSecretis created in addition to your application’s Deployment. - It modifies the Pod spec to include an
initContainer(or sometimes modifies the main container) that starts up, fetches secrets from Vault, and writes them to a sharedemptyDirvolume. - It also modifies the Pod spec to mount this
emptyDirvolume into the main application container.
Your application container then reads the secrets from the mounted volume (e.g., /vault/secrets/creds.txt). The valueFrom.secretKeyRef in your Deployment manifest is a bit of a red herring here if you’re using the injector directly on the Pod. The injector can be configured to populate a Kubernetes Secret resource or to mount secrets directly into the Pod.
The most common pattern with Argo CD and Vault Agent Injector:
- Argo CD deploys your application manifests: This includes your
Deploymentwith Vault annotations. - Vault Agent Injector (Kubernetes Admission Controller) intercepts Pod creation:
- It reads the annotations (
vault.hashicorp.com/agent-inject,vault.hashicorp.com/role,vault.hashicorp.com/agent-inject-secret-*). - It authenticates to Vault (using the Pod’s Service Account and a Vault K8s auth role).
- It fetches the specified secrets from Vault.
- It creates a new Kubernetes
Secretobject in the cluster (e.g.,my-app-creds). The name of this K8s secret is derived from the annotation key (e.g.,creds.txt->creds). - It also modifies the Pod spec to mount this newly created Kubernetes
Secretas a volume.
- It reads the annotations (
- Your application Pod starts: It can now access the secrets either directly from the mounted volume (e.g.,
/vault/secrets/creds.txt) or, if you’ve configured your Deployment to reference the automatically created KubernetesSecret(which is often named based on the annotation), it can usesecretKeyRef.
The key insight is that Argo CD itself doesn’t talk to Vault. It just deploys the Deployment resource. The Vault Agent Injector, a separate Kubernetes component, is the one that performs the dynamic fetching and injection. Argo CD’s role is to ensure the Deployment resource with the correct annotations is present in the cluster.
The next step you’ll likely encounter is managing Vault auth roles and policies to grant your Kubernetes Service Accounts the necessary permissions to read specific secrets.