How to Manage API Keys Across Dev, Staging, and Production

Published March 9, 2026 · SPUNK LLC · 10 min read

Using the same API key across development, staging, and production is one of the most common security mistakes in software teams. It creates a single blast radius where a leaked dev key compromises production data, and it makes debugging nearly impossible when three environments share the same credentials hitting the same endpoints.

This guide covers practical strategies for separating API keys per environment, automating the process, and preventing the most common mistakes that lead to cross-environment key leaks.

Why Environment Separation Matters

Every major API provider offers environment-specific keys for a reason. Stripe has test and live keys. AWS recommends separate IAM users per environment. Twilio provides test credentials with sandboxed behavior. When you use the same key everywhere, you lose three critical properties:

The Three-File Pattern

The most widely adopted pattern for environment separation uses three .env files at the root of your project:

.env.development    # Local dev keys, safe to be lower-permission
.env.staging        # Staging keys, mirrors production permissions
.env.production     # Production keys, highest security

Your application loads the correct file based on a NODE_ENV, RAILS_ENV, or equivalent environment variable. Most modern frameworks handle this natively:

# Next.js loads automatically based on NODE_ENV
# Vite uses .env, .env.development, .env.production
# Rails uses credentials.yml.enc with environment namespaces
# Django uses django-environ with per-environment .env files

Rules for the three-file pattern

  1. Never commit any .env file to version control. Add all three to .gitignore. Instead, maintain a .env.example with placeholder values that documents the required variables.
  2. Use different key prefixes or naming. Name your variables with environment hints: STRIPE_SECRET_KEY_DEV, STRIPE_SECRET_KEY_PROD. This makes accidental cross-use obvious in code review.
  3. Restrict permissions progressively. Dev keys should have minimal scopes. Staging keys should mirror production permissions but point to sandbox environments. Production keys should have the exact permissions needed and nothing more.

Cloud Secret Managers

For teams beyond the solo-developer stage, .env files on local machines become unmanageable. Cloud secret managers centralize key storage and provide access control, versioning, and audit logging.

AWS Secrets Manager

Stores secrets as key-value pairs with automatic rotation support. You create separate secrets per environment using naming conventions like prod/stripe/secret-key and dev/stripe/secret-key. IAM policies restrict which environments each developer or service can access.

Google Cloud Secret Manager

Similar to AWS but integrated with Google Cloud IAM. Secrets are versioned automatically, and you can grant access at the project level to enforce environment boundaries. Each GCP project maps to one environment.

HashiCorp Vault

The most flexible option for multi-cloud teams. Vault supports dynamic secrets that are generated on demand and automatically expire. Instead of storing a static database password, Vault creates a temporary credential with a TTL. When the TTL expires, the credential is revoked. This eliminates the concept of a long-lived key entirely for supported backends.

Doppler and Infisical

Developer-focused secret managers that integrate directly with your deployment pipeline. They sync secrets to your hosting platform (Vercel, Railway, Fly.io) per environment. The key advantage is that developers never download production secrets to their local machines.

CI/CD Pipeline Configuration

Your CI/CD pipeline is the most dangerous place for key management mistakes. Build logs, cached environment variables, and shared runners all create exposure vectors.

GitHub Actions

Use environment secrets, not repository secrets, when you need per-environment keys. Create three GitHub environments (development, staging, production) and assign secrets to each. Reference them in your workflow with the environment key:

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: production
    steps:
      - name: Deploy
        env:
          STRIPE_KEY: ${{ secrets.STRIPE_SECRET_KEY }}
          DB_URL: ${{ secrets.DATABASE_URL }}

This ensures your production Stripe key is only available in jobs that explicitly target the production environment. A PR build running against the development environment cannot access production secrets.

GitLab CI/CD

GitLab supports variable scoping by environment. Define variables in Settings > CI/CD > Variables and set the environment scope to production, staging, or development. Variables are only injected into pipelines that match the scope.

Common CI/CD mistakes

Browser Extension Key Management

If you use a browser-based key manager (like keys.surf), environment separation should extend to your browser workflow. Tag each stored key with its environment so you never accidentally auto-fill a production key into a development dashboard.

Good key managers let you set an active environment profile. When your profile is set to "development," only dev keys appear in auto-fill suggestions. Switching to "production" swaps the entire key set. This eliminates the human error of pasting the wrong key into the wrong console.

Key Rotation Across Environments

Key rotation is harder with multiple environments because you have three times the keys to rotate and three times the places to update them. Automate this process:

  1. Rotate production keys first. Production is your highest-risk environment. Rotate these keys on a fixed schedule (30 to 90 days depending on the service).
  2. Rotate staging keys after production. This lets staging verify that your rotation process works before you apply it to the next production cycle.
  3. Dev keys can be longer-lived but should still be rotated quarterly. Stale dev keys accumulate permissions over time as developers add scopes and forget to remove them.

The Emergency Playbook

When a key leaks (and statistically, it will), having environment separation means you already know the blast radius. Here is a response checklist:

  1. Identify which environment the leaked key belongs to. If your keys are properly separated, this is immediate.
  2. Revoke the leaked key. Do not rotate it. Revoke it immediately and generate a new one.
  3. Check audit logs. Most API providers show when and how a key was used. Look for unauthorized usage between the leak and revocation.
  4. Update the new key in the correct environment only. Do not propagate the incident to other environments by performing an unnecessary mass rotation.
  5. Post-mortem. Determine how the key leaked. Was it committed to git? Logged in a build? Pasted in a Slack message? Fix the process, not just the key.

Practical Checklist

Before your next deploy, verify each of these:

Environment separation is not overhead. It is the minimum viable security posture for any team shipping software with external API dependencies. Set it up once, automate the maintenance, and move on to building features with the confidence that a dev mistake stays in dev.