Cybersecurity · Secrets & Credentials
Secrets Management in 2026: Vault, Doppler, AWS Secrets Manager, and When .env Is Fine
Leaked credentials are the most preventable category of security breach. Here is an honest look at when you need a dedicated secrets manager, which tool to pick, and what to do if you're still on .env files.
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.
Sponsored
More from this category
More from Cybersecurity
Container Security in 2026: Image Scanning, SBOMs, and What Teams Actually Do
Passkeys Are Ready: Implementing Passwordless Auth in Your Web App
Prompt Injection in 2026: The Attack Your AI App Probably Isn't Defending Against
Sponsored
The dispatch
Working notes from
the studio.
A short letter twice a month — what we shipped, what broke, and the AI tools earning their keep.
Discussion
Join the conversation.
Comments are powered by GitHub Discussions. Sign in with your GitHub account to leave a comment.
Sponsored