18.6 Secrets as Environment Variables vs Volume Mounts
Alright, let’s get down to brass tacks. You’ve got your shiny new Secret, and now you need to get its precious data into your Pod. You’ve got two main highways for this: injecting them as environment variables or mounting them as files in a volume. The choice isn’t just about preference; it’s a fundamental decision that affects security, observability, and how your application behaves. Let’s break it down.
The Quick and Dirty: Environment Variables
This is the method everyone reaches for first because it’s dead simple. You define your environment variables in your Pod spec, and Kubernetes magically populates them from your Secret. It feels familiar, especially if you’re coming from a traditional app development background.
apiVersion: v1
kind: Pod
metadata:
name: my-pod-env
spec:
containers:
- name: my-app
image: my-app:latest
env:
- name: DATABASE_PASSWORD # The env var name in your container
valueFrom:
secretKeyRef:
name: my-db-secret # The name of the Secret
key: db-password # The specific key inside the Secret
- name: API_KEY
valueFrom:
secretKeyRef:
name: my-api-secret
key: api-key
Why you might (and often shouldn’t) do this:
It’s incredibly straightforward. Your application just reads os.getenv('DATABASE_PASSWORD') or process.env.API_KEY and carries on. No new code, no fuss. For simple, one-off values, it seems perfect.
The massive, glaring drawbacks:
First, security. Any process in your container can access all its environment variables. If your app gets compromised and an attacker gains shell access, they can just run env or printenv and see every secret you’ve injected this way in plain text. It’s a gold mine.
Second, visibility. Environment variables are part of the Pod’s runtime specification. While kubectl describe pod won’t show the actual values, they are still more exposed in the orchestration layer than they need to be.
Third, and this is the real killer, no dynamism. Once that Pod starts, those environment variables are set in stone. If you update the Secret (kubectl apply -f updated-secret.yaml), the Pod has no idea. It will keep running with the old values until you restart it. In a world where we rotate secrets for security, this is a deal-breaker.
The Robust Way: Volume Mounts
This is where Kubernetes shines. Instead of injecting a value, you inject a file that contains the value. You mount the entire Secret as a volume, and each key becomes a separate file, with the key’s value as the file’s content.
apiVersion: v1
kind: Pod
metadata:
name: my-pod-volume
spec:
containers:
- name: my-app
image: my-app:latest
volumeMounts:
- name: secret-volume
mountPath: /etc/secrets
readOnly: true # This is not just a good idea, it's the law.
volumes:
- name: secret-volume
secret:
secretName: my-db-secret # Mounts the entire secret
Now, your database password isn’t in an environment variable; it’s sitting in a file at /etc/secrets/db-password. Your app just needs to read that file. This solves nearly every problem the environment variable method has.
Why this is almost always the better choice:
- Security: Files have permissions. You can control access via the filesystem, making it harder for a stray process to slurp up all your secrets. The volume is typically mounted as read-only, adding another layer of safety.
- Dynamism: This is the magic part. Kubernetes doesn’t just mount the Secret; it keeps it updated. When you update the Secret, Kubernetes eventually propagates the changes to the written files on the Pod’s filesystem. Your app needs to be smart enough to watch for and reload these changes (e.g., using a library like
fs.watchin Node.js orwatchdogin Python), but the mechanism is there. This enables live secret rotation without pod restarts. It’s a game-changer. - Complex Secrets: Got a TLS certificate that’s actually a multi-line blob? A JSON service account key? Trying to shove that into an environment variable is a recipe for quoting and escaping nightmares. Files handle this with ease.
The Hybrid “Worst of Both Worlds” Approach
Just to show you what not to do, Kubernetes does let you do this. You can mount a specific key from a Secret as a volume, and then use a subPath to mount that single file. But be warned: this breaks the dynamic update feature.
volumeMounts:
- name: secret-volume
mountPath: "/etc/secrets/db-password" # Mounts a single file
subPath: db-password # from the volume
If you update the db-password key in the underlying Secret, the Pod will not see the change because subPath effectively creates a static reference at pod startup. You’ve now combined the complexity of volume mounts with the static nature of environment variables. It’s a trap. Avoid it unless you have a very specific, static need.
So, Which One Should You Use?
Use environment variables only for secrets that are truly static for the lifetime of the Pod and are low-risk. It’s rare, but it happens.
For everything else—which is about 95% of use cases—use volume mounts. Yes, it requires a slight change in your application code to read from a file instead of an environment variable. It’s a trivial change that pays massive dividends in security and operational flexibility. It’s the professional choice. Your brilliant friend (that’s me) insists.