All posts
6 min read

What Is Secret Drift and How to Detect It

conceptsdriftbest-practices

You push STRIPE_SECRET_KEY to Vercel. A week later, you rotate it in the Stripe dashboard and update your local .env file. But you forget to update Convex. Now your frontend (Vercel) has the new key and your backend (Convex) has the old one. Webhooks fail. Payments don't reconcile. You spend two hours tracing the issue before realizing it's a stale secret.

This is secret drift -- when the same logical secret has different values across platforms, environments, or files.

How drift happens

Drift is almost never intentional. It happens through normal development workflow:

1. Manual rotation without full propagation

You rotate a database password and update it in Vercel. You forget Railway. Your background worker silently fails to connect, and the error gets buried in logs.

2. Direct dashboard edits

A teammate updates a variable directly in the Convex dashboard. Nobody else knows. Your local .env is now stale, and so is every other platform that should share that value.

3. Environment promotion gaps

You test a new API key in development and promote it to production -- but only in one platform. The other platforms still have the old key.

4. Onboarding gaps

A new developer gets a .env file from Slack (or 1Password, or a shared doc). By the time they start working, three of the eight values have been rotated.

5. Branch-specific overrides

You temporarily change a secret in a preview deployment for testing and forget to revert it. The preview environment drifts from the development environment it was supposed to mirror.

Why drift is dangerous

Drift bugs are uniquely frustrating because they look like application bugs. The code is correct. The logic is correct. But the runtime behavior is wrong because one of the inputs is stale.

Common symptoms:

  • Silent failures -- API calls return 401s or 403s. Webhooks are rejected. The application doesn't crash; it just does the wrong thing.
  • Inconsistent behavior across environments -- "It works in development but not in production" is almost always a configuration problem, and drift is the most common configuration problem.
  • Debugging misdirection -- You spend hours reading code when the issue is a three-month-old API key in a platform you forgot to update.
  • Security exposure -- A rotated secret that wasn't fully propagated means the old value is still active somewhere. If it was rotated because it was compromised, you still have an exposed credential.

Detecting drift

Detecting drift requires comparing secret values across every place they're supposed to be consistent. There are a few ways to approach this.

Manual audit

Log into each platform, find each secret, and compare values. This works for two platforms with three secrets. It does not work for four platforms with fifteen secrets. And you'll need to do it regularly, not just when something breaks.

Hash-based comparison

Instead of comparing raw values, you can hash each secret and compare hashes across platforms. This tells you whether values match without exposing the values themselves. dotenvy uses this approach internally -- it computes hashes of local values and remote values to detect mismatches.

Automated detection with dotenvy

dotenvy can show you exactly where drift exists:

dotenvy sync test --dry-run

The --dry-run flag compares your local .env.test values against what's currently set on each target platform. It reports which secrets would change without actually modifying anything.

The output looks like this:

Comparing .env.test against 2 targets...

vercel/development:
  STRIPE_SECRET_KEY     ✓ synced
  DATABASE_URL          ✗ drifted (local ≠ remote)
  RESEND_API_KEY        ✗ missing on remote

convex/default:
  STRIPE_SECRET_KEY     ✓ synced
  DATABASE_URL          ✓ synced
  RESEND_API_KEY        ✗ missing on remote

You can immediately see which secrets are out of sync and where. No dashboard hopping required.

To fix the drift, drop the --dry-run flag:

dotenvy sync test

This pushes your local values to all targets, bringing everything back into alignment.

Best practices for preventing drift

1. Never edit secrets directly in platform dashboards

Once you use a sync tool, the local file should be the single source of truth. Editing directly in Vercel or Convex creates an untracked change that will eventually cause drift.

2. Rotate secrets through your sync workflow

When you rotate a key:

dotenvy set STRIPE_SECRET_KEY=sk_test_new_value
dotenvy set STRIPE_SECRET_KEY=sk_live_new_value --env live

This updates your local file and pushes to all targets in one step. No platform is left behind.

3. Run dry-run checks regularly

Make dotenvy sync --dry-run part of your routine -- weekly, or after any credential rotation. Catching drift early is always cheaper than debugging a production incident.

4. Track which secrets you manage

The dotenvy.yaml file acts as a manifest of which secrets your project needs. If a new secret is added to one platform but not to the config, it won't be tracked and drift won't be detected. Keep the config current.

secrets:
  - STRIPE_SECRET_KEY
  - STRIPE_WEBHOOK_SECRET
  - DATABASE_URL
  - RESEND_API_KEY
  - CLERK_SECRET_KEY

5. Use environment mapping consistently

Define clear mappings so you always know which local environment corresponds to which remote environment:

targets:
  vercel:
    type: vercel
    project: my-app
    mapping:
      development: test
      preview: test
      production: live

This eliminates ambiguity. dotenvy sync test always means "push .env.test to Vercel development and preview." There's no guessing.

Drift detection and team workflows

For solo developers, drift is an annoyance. For teams, it's a compounding problem. Every team member who edits a secret independently creates a potential drift vector.

The dotenvy Team dashboard (coming soon) adds drift detection across team members:

  • See which platform environments are in sync and which have drifted
  • Get alerts when a secret value changes on a remote platform unexpectedly
  • Track who last synced each environment

If your team manages secrets across multiple platforms, join the waitlist for early access to the Team dashboard.

The takeaway

Secret drift is one of those problems that's invisible until it causes an outage. The fix isn't more discipline -- it's tooling that makes drift impossible or immediately visible.

Define your secrets in one place. Sync them with one command. Check for drift regularly. The dashboards are for reading; your local file is for writing.

curl -fsSL https://dotenvy.dev/install.sh | sh