Cybersecurity · Secrets & Credentials
Secrets Management in 2026: Vault, Doppler, or AWS
Leaked credentials are the most preventable breach category. When you need a real secrets manager, which one to pick, and what to do if you're still on .env.
Anurag Verma
10 min read
Sponsored
In February 2025, a developer at a fintech startup accidentally pushed their .env file to a public GitHub repository. The file contained API keys for their payment processor, database credentials, and an admin token for their cloud provider. By the time the push was caught, 11 minutes later, automated scanners had already harvested the credentials. The breach cost the company two weeks of incident response and a complete infrastructure rebuild.
This is not an unusual story. GitHub’s own secret scanning system detected over 39 million exposed secrets in public repositories in 2023 alone, according to their State of the Octoverse report. The category of breach “leaked credentials” shows up in virtually every annual security report as a leading initial attack vector. And it’s one of the most preventable problems in software security.
Secrets management is the discipline of handling credentials (API keys, database passwords, private certificates, OAuth secrets), in a way that doesn’t leave them sitting in files, environment variables, or source code where they can be accidentally exposed.
Here is what the options actually look like and how to pick one that fits where you are.
The .env File Reality
Let’s start here because most teams are on .env files and the advice to “never use .env” is not actionable.
.env files are not inherently dangerous. They become dangerous when:
- They’re committed to version control (even accidentally, even briefly)
- They’re copied between machines without access controls
- They’re readable by everyone with shell access to the server
- There’s no rotation process when a secret is compromised
If you’re in a solo project or a two-person team, properly used .env files with clear guardrails are an acceptable starting point:
# .gitignore — this should be the first thing in every repo
.env
.env.local
.env.*.local
*.env
# Show what variables are needed without the values
# Commit this file
.env.example:
DATABASE_URL=
STRIPE_SECRET_KEY=
SENDGRID_API_KEY=
JWT_SECRET=
The .env file that stays out of git, on machines accessed by authorized people, with clear ownership of who rotates secrets when someone leaves. That’s manageable. It’s not ideal for anything beyond small-scale, but it’s not a crisis.
The moment you need to scale past that (more team members, multiple environments, shared infrastructure, compliance requirements), you need proper secrets management.
What Proper Secrets Management Actually Provides
Before evaluating tools, know what you’re buying:
Centralized storage: Secrets live in one place with access controls, not scattered across .env files on different machines.
Access control: Define which service, which person, or which CI job can read which secret. Audit who read what and when.
Rotation: Secrets can be rotated (changed) without touching application code or redeploying. Critical for responding to a credential leak.
Versioning: Keep historical versions of secrets for rollback if a rotation breaks something.
Dynamic secrets: Some tools (Vault), can generate short-lived credentials on demand, a database password that exists for 5 minutes and then expires. Even if leaked, it’s useless.
Integration with your runtime: Inject secrets into containers, Lambda functions, or CI jobs at runtime rather than at deploy time.
HashiCorp Vault
Vault is the most feature-complete secrets manager available. It handles every use case, static secrets storage, dynamic credentials for databases and cloud providers, PKI management, encryption as a service, and SSH certificate signing. If you need something from secrets infrastructure, Vault can probably do it.
What it’s genuinely good at:
Dynamic secrets are Vault’s best feature. Instead of a static database password that’s rotated monthly, Vault generates a unique credential when a service requests it, with an automatic expiry. The service gets credentials for the lifetime of its request, and then they’re gone.
# Vault dynamic database credential example
# Enable the database secrets engine
vault secrets enable database
# Configure a postgres connection
vault write database/config/my-postgres \
plugin_name=postgresql-database-plugin \
connection_url="postgresql://{{username}}:{{password}}@localhost:5432/mydb" \
allowed_roles="my-role" \
username="vault-admin" \
password="admin-password"
# Create a role that generates credentials
vault write database/roles/my-role \
db_name=my-postgres \
creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; GRANT SELECT ON ALL TABLES IN SCHEMA public TO \"{{name}}\";" \
default_ttl="1h" \
max_ttl="24h"
# Application requests credentials at runtime
vault read database/creds/my-role
# Returns a username/password that expire in 1 hour
What it costs:
Vault requires meaningful operational investment. You’re running a stateful service that your entire infrastructure depends on. The Vault cluster needs high availability, backup, and monitoring. A Vault outage means applications can’t fetch new credentials, which may mean they can’t start. HCP Vault (HashiCorp’s hosted offering), removes some of this burden, but it’s not cheap.
The learning curve is real. Vault has a policy language (HCL), an auth method configuration, and a secrets engine architecture that takes time to get right. Setting up Vault correctly for a production environment is a week of work, not an afternoon.
Who it’s for: Teams with significant security requirements, compliance obligations that specify secrets management controls, or organizations already running Kubernetes who can use the Vault Secrets Operator. The Vault Secrets Operator syncs secrets into Kubernetes Secrets automatically, which removes the need for applications to know about Vault at all.
AWS Secrets Manager (and Parameter Store)
If you’re already on AWS, Secrets Manager is often the right default. It integrates with IAM for access control, CloudTrail for audit logging, and Lambda/ECS/EKS for runtime injection.
AWS Secrets Manager vs. Parameter Store:
AWS has two relevant services and the distinction matters:
| Secrets Manager | Parameter Store | |
|---|---|---|
| Cost | $0.40/secret/month + $0.05/10k API calls | Free for Standard tier; $0.05/parameter/month for Advanced |
| Rotation | Built-in rotation with Lambda functions | Manual |
| Secret size | Up to 65KB | Up to 8KB (Advanced) |
| Use case | Credentials, API keys, tokens | Config values, feature flags, secrets |
| Cross-account | Yes | Limited |
For most teams on AWS, use Parameter Store for configuration and Secrets Manager for actual credentials that need rotation.
# boto3: fetch secret at application startup
import boto3
import json
def get_secret(secret_name: str) -> dict:
client = boto3.client('secretsmanager', region_name='us-east-1')
response = client.get_secret_value(SecretId=secret_name)
return json.loads(response['SecretString'])
# In practice, cache this at startup — don't call on every request
db_creds = get_secret('production/myapp/database')
DATABASE_URL = f"postgresql://{db_creds['username']}:{db_creds['password']}@{db_creds['host']}/mydb"
For ECS and Lambda, you can inject Secrets Manager values directly into environment variables at task/function definition time, so application code doesn’t need to know about Secrets Manager at all:
{
"containerDefinitions": [{
"name": "api",
"secrets": [
{
"name": "STRIPE_SECRET_KEY",
"valueFrom": "arn:aws:secretsmanager:us-east-1:123456789:secret:production/stripe-key"
}
]
}]
}
What it costs: Financially modest for most scales. Operationally, much lighter than Vault, AWS manages the infrastructure. The main downside is AWS lock-in. If you move clouds, Secrets Manager doesn’t come with you.
GCP has Secret Manager and Azure has Key Vault with nearly identical capabilities for their respective ecosystems.
Doppler
Doppler is a developer-experience-first secrets management SaaS. Where Vault prioritizes capability and AWS Secrets Manager prioritizes AWS integration, Doppler prioritizes making secrets management not annoying.
The core model: you define secrets in the Doppler dashboard, organized by project and environment (dev/staging/production). Applications fetch their secrets via the Doppler CLI or SDK. The CLI can inject secrets as environment variables at process startup, so existing applications work without code changes:
# Run your app with secrets injected as environment variables
doppler run -- node server.js
# Or generate a .env file for local development (not committed to git)
doppler secrets download --no-file --format env > .env
Doppler handles CI/CD integration natively. There are official GitHub Actions, GitLab integrations, and plugins for CircleCI, Jenkins, and most other CI systems. Rotating a secret means updating it in Doppler; the next deployment or process restart picks up the new value.
The tradeoff: Doppler is a third-party SaaS dependency. Your secrets are stored in Doppler’s infrastructure. For most teams this is acceptable, Doppler is SOC 2 Type II certified and has good uptime history, but it’s a meaningful consideration for organizations with strict data residency requirements.
Who it’s for: Teams that want a significant improvement over .env files with minimal operational overhead. Good default for startups and agencies managing multiple client projects, since the project/environment model maps well to multi-tenant workflows.
The Kubernetes-Specific Path
If you’re on Kubernetes, a few additional options are worth knowing:
External Secrets Operator (ESO): Syncs secrets from any secrets manager (Vault, AWS Secrets Manager, GCP Secret Manager, Doppler, and more) into Kubernetes Secrets. Applications read standard Kubernetes Secrets; ESO handles the syncing from the backend.
# ESO: sync an AWS Secrets Manager secret into a Kubernetes Secret
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: database-credentials
spec:
refreshInterval: 1h
secretStoreRef:
name: aws-secrets-manager
kind: ClusterSecretStore
target:
name: database-credentials
data:
- secretKey: DATABASE_URL
remoteRef:
key: production/myapp/database
property: url
Sealed Secrets: Encrypts secrets using a cluster-specific key so the encrypted form can be committed to git safely. Good for GitOps workflows where you want secrets in the same repository as the rest of your Kubernetes manifests.
Picking a Tool
If you're a solo developer or two-person team:
→ .env with proper .gitignore + secret scanning on your repo
(GitHub has this built-in; enable it in repo Settings > Code security)
If you're a small team (3-15 people) moving beyond .env:
→ Doppler (fastest to adopt, reasonable cost, good DX)
→ AWS/GCP/Azure Secrets Manager if you're already deep in that cloud
If you're running on Kubernetes:
→ External Secrets Operator + whichever backend fits
→ Vault Secrets Operator if you need dynamic credentials
If you have compliance requirements (SOC 2, HIPAA, PCI):
→ AWS Secrets Manager or Vault with audit logging configured
→ Your compliance framework may specify requirements; check before picking
If you need dynamic credentials or advanced PKI:
→ Vault is the only real answer
Baseline Hygiene Regardless of Tool
Whatever you’re using, these practices apply universally:
Enable secret scanning. GitHub, GitLab, and most CI platforms will scan commits for known secret formats (AWS keys, Stripe keys, etc.). Turn this on. It’s free and catches the “accidental commit” scenario before it’s a breach.
Use service accounts, not personal credentials. Applications should authenticate with a service account or IAM role, not a developer’s personal API key. When that developer leaves, you don’t have to hunt down where their credentials are used.
Set expiry dates. API keys and tokens that don’t expire become permanent attack surface. Audit your existing secrets. You’ll find credentials that haven’t been rotated in years.
Document the rotation procedure. When a credential is compromised, you want to rotate it in under an hour. That’s only possible if the procedure is documented and practiced before the emergency.
Secrets management is a process problem as much as a tooling problem. The best secrets manager in the world doesn’t help if developers work around it because it’s inconvenient. Pick something your team will actually use, and make it the path of least resistance.
Frequently asked questions
- Is it ever acceptable to use .env files?
- Yes, at small scale with real guardrails. A solo project or two-person team, with .env in .gitignore, files only on machines authorised people control, and a named owner who rotates credentials when someone leaves, is manageable. It stops being acceptable the moment you add team members, multiple environments, shared infrastructure, or a compliance requirement, because none of those are things a file on a laptop can satisfy.
- What are dynamic secrets and do I need them?
- Instead of storing a database password and rotating it monthly, Vault generates a unique credential when a service asks for one and expires it automatically, often within minutes. The payoff is that a leaked credential is worthless by the time anyone finds it. You need them when the blast radius of a long-lived static credential is genuinely unacceptable. For most teams, static secrets with a documented rotation procedure are a bigger improvement per unit of effort.
- Secrets Manager or Parameter Store on AWS?
- Both, for different things. Parameter Store for configuration values and feature flags, where the standard tier is free and 8KB is plenty. Secrets Manager for actual credentials, where you want built-in rotation through a Lambda, up to 65KB, and proper cross-account access, at $0.40 per secret per month plus $0.05 per 10,000 API calls. Splitting them this way keeps the bill sensible without giving up rotation where it matters.
- What does Vault actually cost to run?
- Roughly a week of work to configure correctly for production, plus ongoing operational load. It is a stateful service that needs high availability, backups, and monitoring, and a Vault outage can mean applications cannot fetch credentials, which can mean they cannot start. There is a policy language and a secrets-engine architecture to learn. HCP Vault removes the infrastructure burden but not the learning curve, and it is not cheap.
- What should I do first if we're on .env files today?
- Turn on secret scanning in GitHub or GitLab, because it is free and catches the accidental-commit case that causes most incidents. Then audit what you have; you will find credentials nobody has rotated in years. Then write down the rotation procedure for your most sensitive secret and actually practise it, because rotating in under an hour during an incident only happens if someone has done it before. Choosing a tool comes after that.
Sponsored
More from this category
More from Cybersecurity
Sponsored
Discussion
Join the conversation.
Comments are powered by GitHub Discussions. Sign in with your GitHub account to leave a comment.
Sponsored