Safeguard
Security Guides•

Environment variables or mounted files for secrets in CI job pods?

Both env vars and mounted files expose a secret to the job. Here is when each fits, what Kubernetes and GitHub docs say, and how to limit exposure.

Safeguard Security Team
3 min read

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

AspectEnvironment variableMounted file
Visible to child processesYes, by inheritanceOnly if they can read the path
Appears in crash dumps and debug outputMore oftenLess often
Easy for tools to consumeVeryNeeds the tool to accept a path
Can be limited with file permissionsNoYes
Risk of being echoed in logsHigherLower

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

  1. Give each job only the secrets it needs, not a shared bundle.
  2. Put the secret in the one container that uses it, not the whole pod.
  3. Never pass it as a command line argument.
  4. Prefer a short-lived token issued per job over a stored long-lived key.
  5. Disable shell tracing in steps that handle secrets, so a debug flag does not print them.
  6. 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

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.