
Vault Agent Injector vs Secrets Operator: A Kubernetes comparison
Vault Agent Injector vs Secrets Operator: A Kubernetes comparison
7 October 2025
Kilian Niemegeerts

This is part 5 of our HashiCorp Vault production series. With Vault running in our tooling cluster, we faced a crucial decision: how do we get those secrets into our application pods?
Series overview:
- Production Kubernetes Architecture with HashiCorp Vault
- Terraform Infrastructure for HashiCorp Vault on EKS
- External Secrets Operator: GitOps for Kubernetes Secrets
- Dynamic PostgreSQL Credentials with HashiCorp Vault
- Vault Agent vs Secrets Operator vs CSI Provider
- Securing Vault Access with Internal NLB and VPN
The 3 HashiCorp Vault Kubernetes integration options
HashiCorp provides three methods to get Vault secrets into Kubernetes:
- Vault Agent Injector
- Uses a mutating admission webhook to inject a sidecar container
- The sidecar authenticates with Vault and fetches secrets
- Secrets are written to a shared volume as files
- Vault Secrets Operator (VSO)
- Uses Kubernetes CRDs to declaratively manage secrets
- Fetches secrets from Vault and creates native Kubernetes Secrets
- Fully GitOps-compatible approach
- Vault CSI Provider
- Uses the Secrets Store CSI Driver
- Mounts secrets as ephemeral volumes during pod startup
- Another CRD-based approach
Our specific requirements
Before choosing, we had to consider our setup:
- Dynamic database credentials that rotate every hour (from part 4)
- GitOps-driven infrastructure using FluxCD
- External Secrets Operator already handling static secrets from Vault
- Zero application changes – developers shouldn’t need Vault knowledge
The key requirement? Automatic lease renewal for dynamic secrets.
Comparing the methods
With our requirements clear, we decided to focus our comparison on the Vault Agent Injector & the Vault Secrets Operator. This is how the options compare:
Feature |
Vault Agent Injector |
Vault Secrets Operator |
|
Secret delivery | Sidecar volume | Kubernetes Secret | |
Dynamic secrets support | ✅ Yes | ❌ No (manual rotation) | |
Lease management | ✅ Automatic | ❌ No | |
GitOps-compatibility | ⚠️ Limited (via annotations) | ✅ Full (via CRDs) | |
Use of native K8s secrets | ❌ No | ✅ Yes | |
Dependency on Vault at pod startup | ✅ Yes | ❌ No | |
Best application | Dynamic credentials | Static configuration |
Why We Chose HashiCorp Vault Agent Injector
The decision was straightforward once we looked at our dynamic database credentials requirement:
- Vault Secrets Operator was out – no automatic rotation for dynamic secrets
- Vault Agent Injector provides automatic lease management out of the box
Since we already use External Secrets Operator for static secrets (perfect for GitOps), we didn’t need VSO’s CRD approach. Vault Agent handles our dynamic credentials beautifully.
The Trade-offs We Accepted
Choosing Vault Agent Injector meant accepting:
- Resource overhead: Every pod gets an extra sidecar container
- Limited GitOps: Configuration via annotations, not CRDs
- Startup dependency: Pods can’t start if Vault is down
For dynamic database credentials with automatic renewal, these trade-offs were worth it. The automatic lease management alone prevents countless rotation-related incidents.
How HashiCorp Vault Agent Injector Works
Here’s the flow when using Vault Agent Injector:
The process is elegant:
- Application pod launches
- Agent-injector detects the pod and injects a Vault Agent sidecar
- Vault Agent authenticates using the pod’s ServiceAccount
- Agent requests secrets (like database credentials)
- Secrets are written to /vault/secrets
- Application reads secrets as regular files
Applications don’t need to know Vault exists – they just read files from a local path.
What’s Next?
With secrets flowing into our pods, how do we keep Vault itself secure? Our next post covers the internal NLB + VPN pattern for securing Vault access.
Continue reading: Kubernetes Vault Integration: Securing AWS Secrets with Internal NLB & VPN
Get the code: Our Vault configuration on GitHub
FAQ
Q: What is the default TTL for database credentials in the setup?
A: We use a default TTL of 1 hour (default_ttl=”1h”) with a maximum TTL of 24 hours (max_ttl=”24h”). This balances security with performance in production environments.
Q: Which PostgreSQL statement does Vault use to create temporary users?
A: Vault executes CREATE ROLE “{{name}}” WITH LOGIN PASSWORD ‘{{password}}’ VALID UNTIL ‘{{expiration}}’ to create temporary database users with an automatic expiration time.
Q: How does Vault connect to the PostgreSQL database?
A: Through a connection URL in the format postgresql://{{username}}:{{password}}@db.example.com:5432/mydb using a dedicated vaultadmin user with appropriate privileges.
Q: What is a lease in the context of Vault database credentials?
A: A lease is Vault’s mechanism for tracking temporary credentials. When an application requests database credentials, it receives them with a lease that determines how long they’re valid and when they’ll be automatically revoked.
Q: Does the database secrets engine store passwords permanently?
A: No, Vault never stores passwords permanently. Credentials are generated on-demand when requested and automatically expire after their TTL period.
Q: Why use 1-hour TTL instead of shorter or longer periods?
A: The 1-hour TTL is our sweet spot in production – short enough to limit exposure if credentials leak, but long enough to avoid performance issues from constant user creation/deletion operations.
Sorry, the comment form is closed at this time.