Neither is safe by itself. An environment variable and a mounted file both hand the secret to everything running in that container. Choose the method your tools support, restrict which container receives it, avoid passing it on a command line, and prefer short-lived credentials so exposure ends with the job.
What the docs say
Kubernetes lets a pod reference a Secret as an environment variable or as a volume mount. Its good practices page adds a point that matters for CI: applications "still need to protect the value of confidential information after reading it" from either source, for example by not logging it or sending it to an untrusted party. It also says that in a pod with several containers, you should define the mount or variable so that only the container that needs the secret can see it.
GitHub's secrets guidance makes a related point. It advises avoiding secrets passed between processes on the command line, because other users can see them with ps and security audit events can capture them, and suggests environment variables, standard input or other mechanisms the target process supports. It also notes that automatic redaction of secrets in logs is not guaranteed, since values can be transformed.
Comparing the two
| Aspect | Environment variable | Mounted file |
|---|---|---|
| Visible to child processes | Yes, by inheritance | Only if they can read the path |
| Appears in crash dumps and debug output | More often | Less often |
| Easy for tools to consume | Very | Needs the tool to accept a path |
| Can be limited with file permissions | No | Yes |
| Risk of being echoed in logs | Higher | Lower |
A file is slightly easier to contain. A variable is simpler. For a build job where the tool only accepts one form, use that form and compensate elsewhere.
Steps that matter more than the choice
- Give each job only the secrets it needs, not a shared bundle.
- Put the secret in the one container that uses it, not the whole pod.
- Never pass it as a command line argument.
- Prefer a short-lived token issued per job over a stored long-lived key.
- Disable shell tracing in steps that handle secrets, so a debug flag does not print them.
- Remove the file or unset the variable as soon as the step finishes.
Do not rely on masking
Log masking helps, but GitHub states that redaction is not guaranteed. Treat any secret that reached a job log as exposed, and rotate it. For more on this, see CI secret masking matches the exact string only.
Where the ephemeral model helps
Because the pod is deleted after the job, a mounted secret does not linger on a disk shared with later jobs. That only holds if the volume is also ephemeral. A file written to a shared persistent volume survives the pod.
Quick check
Is a Kubernetes Secret encrypted?
Kubernetes states that Secret values are stored as base64 and are unencrypted by default, but encryption at rest can be configured. Base64 is encoding, not protection.
Should the secret be in the pod spec as plain text?
No. Reference a Secret object or fetch a short-lived credential at runtime, and avoid committing manifests that contain secret data.
Sources
- Kubernetes: Good practices for Kubernetes Secrets
- Kubernetes: Secrets
- GitHub Docs: Using secrets in GitHub Actions
Last reviewed: 19 October 2026.