A file becomes a Kubernetes Someone copies a DB password into a GitHub Actions secret. A service readsat boot and life is good. Secrets Management Comparison: Env Vars Vs Kms Vs Vault When To Use What?—
Then you grow up a bit:
-
you need to rotate something and you realize rotation is actually a deployment problem
-
you have an incident and discover the secret is in three different places, none of them owned
-
an audit asks “who accessed this secret, when?” and the best you can do is “uh… CloudTrail says someone called
GetSecretValuesometime last week?” -
someone enables debug logs and now your “fine” secret is in a log aggregator you retain for 180 days
This post is about the point where “simple” becomes operational pain.
Environment variables, KMS, and Vault solve different layers of the secrets problem. People argue them like they’re interchangeable. They’re not. The right question isn’t “what’s the best secrets manager?” It’s “how complex is my secrets lifecycle, and what am I willing to operate?”
The real problem behind “secrets management”
Most debates focus on where the secret is stored. That’s the least interesting part.
Secrets management is mostly:
-
creation, distribution, rotation, retirement
-
who/what can read it, under what identity, from where
-
can you rotate without downtime? can you force it? can you roll it back?
-
if it leaks, how bad is it? how fast can you contain it?
-
who accessed what, when, and was it expected?
-
can you revoke quickly and confidently?
And your “secrets” aren’t just API keys.
Real examples I’ve had to babysit at 3am:
- DB credentials (and the “oops it’s shared across 40 services” version)
- Third-party API keys (Stripe, Twilio, SendGrid, etc.)
- TLS certificates (rotation + reload + “why is everything broken”)
- JWT signing keys (rotation without invalidating every session)
- Webhook signing secrets (usually copied around like candy)
-
Kubernetes service account tokens (especially when people treat them as long-lived)
If your secrets are static and rarely rotate, you can keep things simple. If your secrets need frequent rotation, least-privilege boundaries, revocation, and audit trails that make an auditor stop asking questions… you’re in a different game.
Environment variables
What they’re actually good at
Env vars are great at configuration injection. Not “secure storage.” Injection.
They shine when:
-
your secret is short-lived by convention (e.g., ephemeral CI token) and not reused
-
the runtime is already locked down (no shell access, no debug endpoints, no noisy logs)
-
you’re okay with rotate = redeploy
-
you have a small number of secrets per service
Also: env vars are boring. Boring is good.
Why they feel fine at first
Because they are.
Early-stage reality:
-
you deploy once a day (or once a week)
-
you have maybe 3 secrets: DB, Redis, “one API key”
-
you don’t have complicated permission boundaries
-
you don’t have a lot of humans with access to prod shells (yet)
Env vars are “good enough” until they’re suddenly not.
Why they fail at scale
Here’s where I’ve seen env vars turn into a liability.
Leak surface is bigger than people think
Env vars leak in dumb ways:
-
someone logs the entire environment “for debugging”
-
a crash dump or debug endpoint exposes process
-
APM agents capture process metadata
-
Kubernetes and friends can expose values depending on how you inject them
The uncomfortable truth: env vars are easy to accidentally print, and once printed they live forever in log storage.
Rotation becomes “deploy choreography”
Env var rotation usually means:
-
update secret in CI/CD / Kubernetes / task definition
-
redeploy every consumer
-
hope nothing is still running with the old value
-
if it’s a DB password: coordinate server-side change + client deploy + rollback plan
This is fine when you have one service. It’s brutal when you have 40.
I’ve seen teams “rotate” a secret by updating it in one place and forgetting there are old pods still running. Now half the fleet is on old creds, half on new creds, and your incident channel becomes a live reenactment of “eventual consistency.”
Access boundaries are coarse
Env vars don’t give you great answers to:
-
should service A be able to read secret B?
-
can I scope access per endpoint, per environment, per workload identity?
Usually you end up with:
-
“the deployment system can read everything”
-
“the platform team can read everything”
-
and the secret ends up shared because it was convenient
That’s not always evil. It’s just how “we’ll fix it later” becomes permanent.
They encourage long-lived static credentials
Env vars make it easy to keep a credential around for months. So people do.
Static creds + wide distribution + weak rotation = the classic “one leaked secret becomes a multi-service breach.”
Common “this will be fine” assumptions that fail
-
“Only the app can read env vars.”
In practice: anyone who can exec into the container / pod / host can read them. -
“We don’t log env vars.”
Until someone turns on debug logging during an incident. -
“Rotation is easy, we’ll just redeploy.”
Sure unless you’re rotating a DB user shared by many services, during peak traffic, with no connection draining. -
“Kubernetes Secrets are encrypted, so it’s safe.”
Encryption-at-rest doesn’t fix runtime exposure or access sprawl.
KMS
What KMS actually does well in practice
A cloud KMS is excellent for:
-
generating and protecting encryption keys
-
envelope encryption
-
access control around cryptographic operations
-
audit logs for key usage (not secret usage, but key usage)
In real systems, KMS is the backbone for “encrypt stuff and control who can decrypt it.”
The practical win: you don’t have to build crypto key handling yourself. You shouldn’t. You will mess it up in creative ways.
Where teams misuse KMS as a “secrets manager”
I’ve seen people do this:
-
store secrets as encrypted blobs in S3 / Git / a database
-
decrypt at runtime using KMS
-
call it “secrets management”
That can work! But it’s not the same as a secrets system.
What you’ve built is: encrypted configuration storage.
The missing parts are usually:
-
rotation workflows
-
per-secret audit trails
-
dynamic credentials
-
revocation and leases
-
secret-level access policies
KMS doesn’t give you those out of the box.
Clear criteria for when KMS is enough
KMS is “enough” when:
-
your secrets are mostly static and rotate rarely (or rotation is handled elsewhere)
-
you just need secure at-rest storage for an encrypted blob
-
access patterns are simple: “service X can decrypt blob Y”
-
you can tolerate rotate = redeploy (because the decrypted secret is loaded at boot)
-
audit requirements are satisfied by key usage logs + surrounding app logs/process
Concrete examples where KMS is a solid choice:
-
an encrypted config file in S3 that contains API keys and is decrypted by the service at startup
-
encrypted Kubernetes secrets via a mechanism like “sealed secrets” or external encryption tooling
-
an app that uses envelope encryption: data keys encrypted by a KMS key, and plaintext keys never stored
What KMS does not solve
KMS does not solve:
-
secret distribution safely at runtime
-
automatic rotation
-
dynamic credentials
-
leases / TTLs / revocation
-
fine-grained secret-level policy (beyond “can decrypt this blob”)
-
human workflows (break-glass access, approvals, scoped access)
Also: KMS audit logs usually tell you that a decrypt happened, not whether it was “the DB password” vs “the Stripe key” unless you’ve structured everything extremely carefully.
So if your main pain is “rotation is killing us” or “we need per-service dynamic DB creds” or “we need to revoke fast during incidents,” KMS alone won’t get you there.
Vault
People describe Vault as “a place to store secrets.” That description is how you end up installing Vault and still suffering.
Vault is a secrets control plane.
The magic isn’t storage. The magic is what happens around the secret:
-
dynamic credentials
-
leases + TTL
-
revocation
-
identity-based access
-
audit logs at the secret operation level
Dynamic credentials: the feature that changes the game
If you’ve only used Vault to store static key/value pairs, you’re missing the point.
The best day-one Vault use case, in my experience:
-
database secret engine
-
service authenticates to Vault using its workload identity
-
Vault generates a unique DB user/password for that service
-
credentials have a TTL (e.g., 1h, 24h)
-
Vault can revoke it instantly
-
Now “leaked DB password” goes from “drop everything, rotate prod DB, redeploy 30 services” to “revoke that lease, investigate, move on.”
That’s a different incident class.
Leases & TTL: forcing rotation by default
Vault pushes you into a healthier default:
-
credentials expire
-
renewal is automated
-
stale secrets die on their own
-
rotation becomes continuous, not an event you dread
This reduces the number of “ancient secrets” haunting your infrastructure.
Identity-based access
Vault’s access story is much cleaner when you integrate it properly:
-
workloads authenticate using something like Kubernetes auth, cloud IAM auth, etc.
-
policies map identities to what they can read/generate
-
access becomes “this service in this namespace can request DB creds for this role”
That’s the kind of boundary env vars don’t naturally provide.
Audit at the secret level
Vault can produce logs like:
-
which identity requested which secret path
-
when
-
from where (depending on setup)
-
whether it succeeded
That’s extremely useful in incident response and audits.
Strong signals you genuinely need Vault
I’m opinionated here. If any of these are true, Vault becomes very attractive:
-
you need dynamic DB creds (or any dynamic secrets)
-
you need rapid revocation during incidents (minutes, not “next deploy”)
-
your org has many services and rotation is becoming a recurring outage generator
-
you’re in a regulated environment where auditability is a real requirement, not a checkbox
-
you need central policy and ownership because “everyone has secrets everywhere” has become unmanageable
-
you need to manage certificates at scale (PKI) with sane renewal
The honest part: overhead and sharp edges
Vault is not “free security.” It’s an operational commitment.
Things I’ve seen go wrong:
-
Vault becomes a critical dependency and you haven’t designed for its availability
-
people run it like a side project and then act surprised when it causes an outage
-
auth integration is half-done, so humans bypass it and paste tokens around
-
secret engines are powerful, but misconfiguration can cause outages just as fast as it prevents them
Also: running Vault well means thinking about:
-
HA storage backend and failure modes
-
unseal process (or auto-unseal) and operational access
-
upgrade strategy
-
backup/restore and disaster recovery
-
monitoring, alerting, performance
If your team can’t reliably operate a small stateful control plane, “Vault everywhere” can hurt more than it helps.
Side-by-side, in the way that matters
Here’s how I think about it in practice, without pretending there’s a single winner.
Rotation
-
Env vars
rotation is usually “change value + redeploy everything.” Easy to do badly.
-
KMS
rotation depends on what you’ve built around it. Often still redeploy-based.
-
Vault
rotation can be continuous via TTLs; you can revoke and renew without forcing redeploys (if your apps are built for it).
Blast radius
-
Env vars
high risk of shared credentials and broad distribution.
-
KMS
blast radius depends on blob design; can be okay, but often coarse.
-
Vault
strong potential for per-service/per-instance credentials, reducing blast radius dramatically.
Incident response
-
Env vars
“find everywhere it’s used, rotate it, redeploy, pray.”
-
KMS
“disable decrypt permissions” is a sledgehammer; you might break more than you intended.
-
Vault
revoke leases, disable a role, trace access by identity. Much more surgical.
Auditability
-
Env vars
basically none, unless you wrap everything in process and hope people follow it.
-
KMS
key usage logs are helpful, but you may lack secret-level clarity.
-
Vault
designed to answer “who accessed what” at the secret operation level.
Operational complexity
-
Env vars
simplest. Still not “safe,” but simple.
-
KMS
moderate. You rely on cloud primitives but you still need patterns.
-
Vault
highest. Powerful, but you’re operating a real system with real consequences.
If you take one thing from this: Vault buys you lifecycle control. KMS buys you crypto primitives. Env vars buy you simplicity.
Pick based on which pain you’re actually having.
Real-world architectures that actually work
Small teams
What works:
-
env vars for delivery
-
secrets stored in a managed secret store (or encrypted config blob)
-
KMS behind the scenes (usually via your cloud provider)
-
strict discipline around logging + access
Why it makes sense:
-
you don’t want to operate Vault yet
-
rotation events are rare enough that redeploying isn’t traumatic
-
the team can keep ownership simple
The trap to avoid:
-
one shared DB user for everything
-
no plan for rotation
-
“temporary” secrets copied into too many places
Growing SaaS orgs
What works:
-
managed secrets store or KMS-encrypted blobs for static secrets
-
start introducing Vault where it actually pays off:
-
DB dynamic creds
-
PKI for internal TLS
-
high-risk third-party keys with strict access policies
-
-
push apps toward reload/renew patterns
Why it makes sense:
-
you’re starting to feel lifecycle pain (rotation and access sprawl)
-
you can justify operational overhead for the highest-value secrets first
-
you can keep a migration path instead of a big-bang rewrite
Regulated / security-heavy environments
What works:
-
Vault (or equivalent control plane) as a core component
-
dynamic secrets wherever possible
-
strong identity integration
-
audit pipelines treated as first-class
-
break-glass workflows that are explicit and tested
Why it makes sense:
-
audit and incident requirements are non-negotiable
-
secret-level access tracking matters
-
rotation cadence is usually mandated
The trap to avoid:
-
installing Vault and then still using static creds everywhere because “migration is hard”
-
rotating aggressively without a deployment strategy (you will create outages)
A migration path that minimizes pain
The mistake I see a lot: teams jump from env vars straight to “Vault everywhere” because they’re scared. That usually creates a new category of outages.
A calmer progression that works:
Stop the bleeding
-
scrub secrets from logs (seriously, do this first)
-
inventory where secrets live
-
assign ownership per secret and per system
-
reduce obvious blast radius (split shared credentials)
Centralize storage
-
use a managed secrets store or KMS-encrypted blobs
-
pull secrets at deploy time and inject into env vars
-
get a single source of truth and access control
This step alone reduces chaos.
Make rotation a normal operation
-
build redeploy automation around rotation
-
support dual secrets during rotation windows (old/new)
-
measure how long rotation takes end-to-end
If rotation is “special,” it will break things.
Introduce Vault where it’s clearly worth it
Start with the high ROI:
-
DB dynamic credentials
-
PKI for internal certs
-
anything where revocation matters during incidents
Evolve app patterns
Vault only really shines when apps can:
-
renew credentials
-
reload without restarts (or restart safely)
-
handle secret expiry gracefully
This is where “platform engineering” becomes “actually engineering.”
Common migration mistakes
-
migrating secrets storage without fixing logging (now you just leak “better” secrets)
-
rotating secrets faster than you can deploy (guaranteed outages)
-
treating Vault as a key/value store and never adopting dynamic creds
-
not planning for Vault availability (your control plane needs a control plane mindset)
Mistakes I’ve personally seen
“We use Vault”
This is the saddest version.
Vault becomes just another place to copy/paste secrets from, and you keep all the rotation pain. If you’re not using TTLs, leases, revocation, or dynamic engines where appropriate, you’re paying overhead for marginal benefit.
Over-rotating without a deployment strategy
Rotation cadence isn’t virtue. Rotation that breaks prod is just chaos with good intentions.
If you rotate DB creds every 24 hours but your deploy pipeline takes 3 days to roll out everywhere, you’ve built an outage generator.
Treating env vars as secure storage
Env vars are a delivery path. They are not a guarantee.
If your threat model includes “someone got into a container,” env vars are already compromised.
No clear ownership
This causes the worst incidents.
When a secret leaks, you need to answer quickly:
-
who owns it?
-
who can rotate it?
-
what depends on it?
-
how do we roll it out safely?
If the answer is “uh… maybe the platform team?” you’re going to have a long day.
You Might Be Interested In
- Cloud Egress Control: How To Stop Data Exfiltration Via Outbound Traffic
- Policy-as-code With Opa: A Practical Starter Kit
- Terraform Security Checks: Catching Risky Infrastructure Before Apply
Conclusion
Terraform security isn’t about catching every possible misconfiguration or building a perfect policy set on day one. It’s about stopping the mistakes that actually hurt you, at the moment when they’re still easy to fix. In practice, that means focusing on plan-time visibility, blocking only high-impact risks, and accepting that some exceptions will always exist.
The teams that get this right don’t have the most scanners or the strictest rules. They have clear ownership, policies tied to real incidents, and guardrails that feel fair to the people using them. When security checks align with how engineers actually work, they stop being obstacles and start being quiet protection.
If your Terraform security feels painful or ineffective, it’s usually not because you need more tools. It’s because the checks are in the wrong place, enforcing the wrong things, or trying to solve problems that don’t happen in the real world. Get those right, and most of the hard problems disappear before ever runs.
FAQs about Secrets Management Comparison: Env Vars Vs Kms Vs Vault When To Use What?
What is the difference between KMS and secrets manager?
KMS is fundamentally about key management and cryptographic operations, not about managing secrets as first-class objects. It generates, stores, and controls access to encryption keys and is typically used to encrypt or decrypt data, often indirectly through envelope encryption. When you “store secrets with KMS,” what you’re really doing is storing encrypted blobs somewhere else and using KMS to unlock them.
A secrets manager, on the other hand, is designed around the lifecycle of secrets themselves. It gives you versioning, rotation workflows, access policies per secret, and often native integrations with services that consume those secrets. In practice, KMS protects the keys, while a secrets manager handles distribution, rotation, and access patterns. Teams often use both together, even if they don’t realize it.
Which approach is most effective for managing secrets used in infrastructure as code tools?
For infrastructure as code tools, the most effective approach is usually centralized secrets storage with controlled injection, not embedding secrets directly in state files or configuration. Tools like Terraform or CloudFormation work best when they reference secrets by name or path and fetch them at runtime from a managed system, rather than handling raw secret values themselves.
In practice, this often means using a secrets manager or Vault as the source of truth, with the IaC tool only responsible for wiring permissions and references. KMS alone can work if you’re encrypting static values, but it quickly becomes awkward once rotation or shared state is involved. The key goal is keeping secrets out of version control and state backends while still making deployments repeatable.
What is the difference between vault and KMS?
KMS is a cryptographic primitive. It’s very good at creating and protecting keys, enforcing who can encrypt or decrypt data, and producing audit logs for those key operations. It does not understand what a database credential is, how long it should live, or how to rotate it safely without downtime.
Vault is a secrets control plane. It understands identities, secret types, leases, expiration, and revocation. Instead of just decrypting a value, Vault can generate credentials on demand, expire them automatically, and revoke them instantly during an incident. KMS is often a building block underneath Vault, but Vault operates at a much higher level of abstraction focused on lifecycle management.
What is the difference between AWS vault and secrets manager?
AWS Secrets Manager is a managed service focused on storing, rotating, and retrieving static secrets like API keys or database passwords. It integrates well with AWS services, supports automatic rotation for some resource types, and removes a lot of operational burden. Its model works well when secrets are relatively static and access patterns are straightforward.
AWS Vault (or tools commonly referred to as “AWS Vault”) is not a secrets manager in that sense. It’s typically used to manage AWS credentials and role-based access for humans, helping engineers assume roles securely without long-lived access keys. One manages application secrets, the other manages human access to AWS itself. Confusing the two leads to systems where human credentials are handled well but application secrets are still scattered and fragile.
What is the difference between KMS and SSE?
KMS is a service that manages encryption keys and access policies, while SSE (Server-Side Encryption) is a feature that uses encryption automatically on a storage service like S3. When you enable SSE, AWS handles encryption and decryption transparently when data is written or read.
When SSE is backed by KMS (SSE-KMS), KMS controls the keys and access permissions behind that encryption. SSE by itself doesn’t give you lifecycle management, rotation logic, or insight into how secrets are used it just ensures data at rest is encrypted. KMS determines who can use the key; SSE determines where and how encryption is applied.

