I am a...
Learn more
How it worksPricingFAQ
Account
May 17, 2026 · 11 min read · Cadence Editorial

How to handle secrets in production

production secrets management — How to handle secrets in production
Photo by [Brett Sayles](https://www.pexels.com/@brett-sayles) on [Pexels](https://www.pexels.com/photo/server-racks-on-data-center-4508751/)

How to handle secrets in production

Production secrets management means storing API keys, database URLs, signing keys, and tokens in a dedicated secrets manager (Doppler, Infisical, 1Password Secrets Automation, AWS Secrets Manager, or HashiCorp Vault), injecting them at runtime instead of build time, scoping access per environment, and rotating on a schedule. The highest-impact first move is killing the .env file in your repo and replacing it with a managed source of truth.

If you take one thing from this post: a secret that exists in .env, .env.local, a Slack DM, a Notion doc, or a screenshot in a bug report is already compromised. Treat it as rotated.

Why secrets keep leaking in 2026

Most production secret leaks are not from sophisticated attackers. They come from four boring places:

  1. A junior developer commits .env because the .gitignore was missing a line.
  2. A CI log prints process.env during a failed deploy.
  3. A founder forwards a Stripe restricted key over email so a contractor can test.
  4. An ex-employee still has access because the only "rotation" plan was "we'll change it if someone leaves."

GitHub's Secret Scanning service detected over 39 million leaked secrets across public repos in 2024 alone. Of those, the most common were OpenAI keys, AWS access keys, and database connection strings. The pattern has not changed in 2026; the volume has gone up because more code is generated by AI and more AI agents write files that include sample env data.

The shift that matters: every production app team, even a two-person startup, now ships AI agents (Cursor, Claude Code, Copilot) that read and write code locally. Any secret on a developer laptop is one careless prompt away from being pasted into a chat window or committed to a side branch. Centralized secrets management is no longer a nice-to-have at series A; it is the default at week one.

The .env anti-patterns you need to kill

A .env file is fine as a local dev convenience. It is not a production strategy. Here are the specific failure modes:

  • Committed .env files. Even on a private repo, that file lives in git history forever. Anyone who clones the repo in 2030 still has the 2026 Postgres password.
  • Shared .env.production in a Notion page. Now the secret lives in a SaaS you didn't audit, accessible to anyone with the link.
  • Build-time secret injection. If your secrets are baked into the build artifact (e.g. Next.js NEXT_PUBLIC_* vars or a Docker image), they are not secrets anymore. Anyone who downloads the artifact has them.
  • Single .env for all environments. Staging and prod sharing the same Stripe key means a staging bug bills real customers.
  • No .env.example. Onboarding a new engineer becomes "DM me the env file," which becomes Slack history forever.
  • Manual updates across machines. Three developers each have a slightly different .env, none of which match production, and one of them ships a feature that works on their machine and breaks on Vercel.

If any of these describe your setup, the next deploy is a good moment to fix it. While you are at it, you may want to read our companion playbook on managing technical debt in a startup, because secret hygiene is the canonical example of debt you must pay down before it pays you.

Pick a secret manager and commit

There are five credible options in 2026. Pick one based on team size, cloud provider, and whether you already pay for a password manager. The honest comparison:

ToolBest forFree tierStrengthsWeaknesses
DopplerStartups on Vercel/Render/NetlifyYes, up to 5 usersCleanest DX, native integrations with most PaaS, instant rotation API, good audit logPricing scales fast once you cross 10 seats
InfisicalTeams that want open-source + self-host optionYes, generousOpen source, can self-host, end-to-end encryption, PR-style change reviewsYounger product, fewer turnkey integrations than Doppler
1Password Secrets AutomationTeams already on 1PasswordNo, requires Business planSingle vendor for human + machine secrets, excellent CLI (op), no separate audit surfaceCosts extra on top of 1Password, less developer-native
AWS Secrets ManagerAll-AWS shops, regulated industriesNo, $0.40 per secret per monthDeep IAM integration, automatic rotation for RDS/Redshift, KMS-backedAWS-only, manual rotation for non-AWS services, no nice UI
HashiCorp VaultEnterprise, multi-cloud, dynamic secretsOSS yes, Enterprise paidMost powerful (dynamic credentials, PKI, transit encryption), audit-gradeHeavy to operate, real DevOps cost, overkill below 50 engineers

A reasonable default for most startups in 2026: Doppler if you want SaaS, Infisical if you want open source, AWS Secrets Manager if your entire stack already lives on AWS. Vault is the right answer once you have a platform team. 1Password Secrets Automation is the right answer if your CEO already enforces 1Password for everyone.

Vercel, Render, and the PaaS env-var trap

Every modern hosting platform has an "Environment Variables" UI: Vercel, Render, Netlify, Fly.io, Railway. They all work for a single-environment side project. They all start breaking the moment you have staging + prod + preview + three developers.

The traps:

  • No diff. When someone edits a var in the Vercel dashboard, nobody else knows. Two weeks later prod breaks and you spend three hours figuring out which var changed.
  • No audit log on the free tiers. Render's free tier does not log who changed what. Vercel's audit log is Enterprise-only on most plans.
  • Easy to copy prod into preview. "Use Production Environment Variables" toggles are dangerous; one click and your PR preview can charge real cards on Stripe live mode.
  • No rotation hooks. When you rotate a key in AWS, your Vercel env does not know. You rotate manually and pray.

The clean pattern: use the PaaS env UI as the delivery surface, not the source of truth. Source of truth lives in Doppler / Infisical / AWS Secrets Manager. A sync integration pushes to Vercel/Render whenever the source updates. Most secret managers have first-class Vercel and Render integrations; configure once and the env vars are read-only in the PaaS UI thereafter. If you are setting up a fresh deploy, our guide on deploying Next.js on Render walks through exactly this pattern.

Separate prod, staging, and dev like they are different companies

A surprising number of teams use the same Stripe key, same OpenAI key, and same database URL across all environments "because it's easier." It is easier until a load test on staging burns through your OpenAI quota or a seed script wipes prod.

The minimum environment separation:

  1. Three projects in your secret manager: dev, staging, prod. No shared values. Even the same Postgres host gets a different connection string because it is a different database.
  2. Separate Stripe accounts. Use Stripe's test mode for dev and staging, live mode only for prod. Restricted keys instead of unrestricted secret keys where the scope allows.
  3. Separate OpenAI / Anthropic / Gemini keys per environment with per-key spend limits. A runaway dev script with the prod key has cost teams five-figure overnight bills more than once.
  4. Separate webhook signing secrets. When you replay a webhook from staging to prod accidentally, the signature mismatch saves you. The same secret would let it through.
  5. Different KMS keys per environment. This matters for compliance audits later, even if it feels like overkill today.

If you are wiring up Stripe webhooks correctly, this separation is what makes test replays safe and prod traffic verifiable.

Rotation is a schedule, not an incident response

The default "we'll rotate when someone leaves" plan does not work. By the time HR tells engineering that a contractor is off, the contractor has had the keys for six weeks past their last commit.

A working rotation cadence:

  • Database passwords: every 90 days. Most managed Postgres (RDS, Supabase, Neon) support rotation without downtime via connection pooling.
  • API keys for third-party services (Stripe, OpenAI, Resend): every 180 days, or immediately on any suspected compromise.
  • JWT signing secrets: every 90 days, with a rolling overlap window so live sessions don't break.
  • Cloud IAM credentials: every 30 days, ideally replaced by short-lived OIDC tokens (Vercel, GitHub Actions, and most CI platforms support this in 2026).
  • Anything you cannot rotate without downtime: put on the roadmap to make rotatable. Inability to rotate is itself a P1 finding.

Vault and AWS Secrets Manager can generate dynamic secrets: a fresh database credential per request, valid for 15 minutes. If you are already on AWS or running Vault, dynamic secrets eliminate most of the rotation problem entirely.

Access control: deployer-only versus app-only

Most teams give every engineer read access to all production secrets because "we trust each other." This is fine until you have eight engineers and one of them runs printenv | curl in a Cursor agent debugging session.

The cleaner model is deployer-only versus app-only:

  • Deployer-only secrets: things the build pipeline needs (deploy keys, container registry credentials, CDN purge tokens). No human has read access; only the CI service principal does. If a developer needs to redeploy, they trigger CI rather than holding the secret.
  • App-only secrets: things the running app needs (database URL, third-party API keys). The runtime fetches them from the secret manager via a service account. Humans see metadata (name, last rotated, environment) but not the value, unless they explicitly request access through a break-glass workflow that logs to a security channel.
  • Human-only secrets: things humans need to read (admin dashboard passwords, vendor portal credentials). These live in 1Password, not in your runtime secret manager.

This split solves the "ex-employee still has access" problem by default: revoking their SSO removes their human-only access, and they never had app-only access to begin with.

Audit logs and git history scrubbing

Two table-stakes practices most teams skip:

Audit logs. Every secret read, write, or rotate event needs to land in a log you can query later. Doppler, Infisical, AWS Secrets Manager, and Vault all produce these natively. Pipe them to your existing logging stack (Datadog, Axiom, BetterStack, CloudWatch) so they survive your secret manager being compromised. When you eventually have a breach, the first question your insurer asks is "show me the audit trail." If you cannot, your payout shrinks.

Git history scrubbing. If a secret has ever been committed, rotating the secret is the first step; scrubbing git history is the second. Use git filter-repo (the modern replacement for git filter-branch) or BFG Repo-Cleaner. Force-push the cleaned history, invalidate forks, and notify collaborators. Critically, scrubbing without rotating is theater; the secret is already in someone's local clone or a forked PR. Always rotate first.

GitHub's Push Protection (now default on new repos as of 2025) blocks most known-format secrets at push time. Enable it on every repo. It catches the obvious cases before they enter history at all.

What to do this week

If you are starting from .env files and Vercel env vars, here is the realistic four-step migration most teams can ship in one focused week:

  1. Pick one secret manager from the table above. Don't agonize for a month; Doppler and Infisical both have 15-minute setup paths.
  2. Migrate prod first, then staging, then dev. Keep the PaaS env-var UI as the delivery surface only.
  3. Set rotation calendar reminders for every key. Pick a quarterly day-of-month so it is predictable.
  4. Run gitleaks or trufflehog against your repo history. Anything it finds gets rotated this week, no exceptions.

If your team is shipping fast and nobody owns this, a Cadence Senior engineer at $1,500/week can migrate a typical Next.js + Postgres stack to Doppler or AWS Secrets Manager in three to five days, including the audit log integration and CI rewiring. You can audit your stack with Ship or Skip first to see whether secrets management is actually your top-priority gap, or whether something like E2E test coverage deserves the week instead.

Trying to figure out who should own this? Every engineer on Cadence is AI-native by default, vetted on Cursor, Claude Code, and Copilot fluency in a live voice interview before they unlock bookings. Senior tier ($1,500/week) handles infrastructure migrations like secrets management end-to-end, with a 48-hour free trial so you only pay if the first commit lands clean.

FAQ

How long does it take to migrate from .env files to a real secrets manager?

For a typical startup with one app, three environments, and under 20 secrets, plan on two to four days of focused work. Most of that is CI rewiring and verifying nothing broke in staging; the actual import is a 30-minute step.

Is AWS Secrets Manager worth $0.40 per secret per month?

For most production apps with 20 to 50 secrets, you are spending $8 to $20/month. That is cheaper than the engineering time of one incident caused by a leaked .env. The cost is rarely the deciding factor; the deciding factor is whether you are already on AWS.

Should I use a secrets manager if it is just me building a side project?

No. A .env.local in .gitignore plus the Vercel/Render env UI is fine for a one-person side project. Move to a real secret manager when you add your second person, or when you start handling user data subject to any compliance regime.

Can I store secrets in environment variables on the host directly?

You can, but you lose audit logs, rotation, and easy revocation. It works for a single-VM hobby setup. It does not work the moment you have two servers, a CI pipeline, and a need to rotate a key without redeploying.

What about .env files for local development with multiple engineers?

Use Doppler's or Infisical's CLI (doppler run / infisical run) to inject dev secrets at runtime without ever writing them to disk. New engineers get access via SSO; revoke their account and their laptop loses access on next run. No more "DM me the env file."

How do I handle secrets in GitHub Actions or other CI?

Use OIDC where the cloud provider supports it (AWS, GCP, Azure, Vercel all do in 2026). This eliminates long-lived CI secrets entirely. For services that still require static tokens, store them in GitHub Actions Secrets or your secret manager's CI integration, never in workflow files.

All posts