Secrets Management for Developers: How to Handle API Keys and Passwords Properly

API keys, database passwords, and JWT secrets require specific handling at every stage. Here's the complete guide to managing secrets without leaking them.

Mahmudul Haque Qudrati — CEO & ML Engineer at Pristren

Mahmudul Haque Qudrati

CEO & ML Engineer

May 17, 2026
10 min read
Secrets Management for Developers: How to Handle API Keys and Passwords Properly

Secrets management has one non-negotiable rule: secrets never go into git. Everything else is tradeoffs between convenience, cost, and security posture appropriate for your team's size and risk profile. This guide covers what not to do, what to do instead at each stage (local development, CI/CD, and production), and how to handle the situation when a secret has already leaked.

What Not to Do

One AI engineering post, weekly

LLM benchmarks, prompt techniques, and token-cost breakdowns — not another AI news roundup.

Never commit .env files with real values. This is the most common mistake. An .env file committed to a git repository - even a private one - creates permanent risk. Git history persists. Former employees retain access to cloned repositories. Repository access controls change.

Never hardcode secrets in source code. const API_KEY = "sk-live-..." in a TypeScript file is the same as committing it to git. Even if you "remember to remove it before pushing," you won't always remember.

Never share secrets over Slack, email, or chat. Chat logs persist. They're searchable. They often sync to cloud services with broader access than your secrets store.

Never use the same secret across environments. Production database credentials should be different from staging credentials. Development API keys should be different from production API keys. When a secret leaks, you want to rotate only the affected environment, not all environments.

Local Development: .env.local in .gitignore

The standard pattern for local development is a .env.local file that is explicitly ignored by git.

Create .gitignore (or add to it):

.env
.env.local
.env.development.local
.env.test.local
.env.production.local

Create .env.example (committed to git, contains no real values):

DATABASE_URL=mongodb://localhost:27017/myapp
JWT_SECRET=your-jwt-secret-here
ANTHROPIC_API_KEY=your-anthropic-key-here
STRIPE_SECRET_KEY=sk-test-...

Create .env.local (not committed, contains real development values):

DATABASE_URL=mongodb://localhost:27017/myapp_dev
JWT_SECRET=a-real-random-secret-for-local-dev-only
ANTHROPIC_API_KEY=sk-ant-...
STRIPE_SECRET_KEY=sk-test-...

New team members clone the repo, copy .env.example to .env.local, and fill in the values from your team's password manager or secrets store.

Team workspace

Ship faster with chat, meetings, and projects in one place — Zlyqor.

Start free

CI/CD: Environment Variables in GitHub Actions

In GitHub Actions, store secrets in the repository or organization secrets store (Settings > Secrets and variables > Actions) and reference them in workflows:

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Deploy
        env:
          DATABASE_URL: ${{ secrets.DATABASE_URL }}
          JWT_SECRET: ${{ secrets.JWT_SECRET }}
        run: pnpm deploy

GitHub Actions secrets are:

  • Encrypted at rest with libsodium
  • Never exposed in logs (GitHub redacts them if they appear)
  • Scoped to the repository or organization
  • Not accessible to pull requests from forks (protecting against supply chain attacks)

For organization-wide secrets shared across multiple repositories, use organization-level secrets in GitHub.

Production: A Secrets Manager

For production deployments, environment variables set in a hosting platform (Vercel, Railway, Coolify, Fly.io) are the minimum viable solution. They store secrets encrypted and inject them at runtime.

For teams that want a dedicated secrets manager:

Doppler is the practical choice for small teams. It provides a UI for managing secrets across environments (dev, staging, production), syncs secrets to your hosting platform, has a CLI for local development (doppler run -- pnpm dev), and audits who accessed what and when. Pricing: free for early-stage teams (up to 5 users and 5 projects), $6/user/month after that.

AWS Secrets Manager is the choice for teams already on AWS infrastructure. It stores secrets encrypted, rotates them automatically, and integrates with IAM for fine-grained access control. Cost: $0.40/secret/month plus API call costs.

1Password Secrets Automation integrates with 1Password, which many teams already use as their password manager. Secrets are available via CLI, SDKs, or direct injection into CI/CD environments. Cost: $19.95/month for teams.

HashiCorp Vault is the self-hosted option for teams that cannot use cloud secrets managers for compliance reasons. Complex to operate, powerful, free to self-host.

The Accidental Commit Problem

If a secret is committed to git, treat the secret as compromised immediately, even if the repository is private.

Step 1: Rotate the secret immediately. Generate a new API key, change the database password, regenerate the JWT secret. Do this before anything else. The old secret is no longer safe.

Step 2: Remove the secret from git history. git rm removes the file from the next commit but not from history. To purge from history, use git filter-repo (the modern replacement for BFG):

pip install git-filter-repo
git filter-repo --path .env --invert-paths
git push --force-with-lease

All collaborators will need to re-clone the repository after a force push. The history rewrite propagates to the remote but any existing clones retain the old history.

Step 3: Audit access. Check your secret provider's audit logs for any activity with the compromised key between when it was committed and when you rotated it.

Detecting Secrets Before They're Committed

gitleaks scans git history and staged changes for secrets:

brew install gitleaks
gitleaks detect --source .

Integrate as a pre-commit hook to catch secrets before they're committed:

# .git/hooks/pre-commit
gitleaks protect --staged

GitHub secret scanning automatically scans repositories for known secret patterns (AWS keys, Stripe keys, GitHub tokens) and alerts you if it finds them. This is enabled by default on public repositories and available for private repositories on GitHub Advanced Security.

Secret Rotation Policy

Secrets should be rotated regularly, not just when they leak. A practical rotation schedule:

  • Production database passwords: every 90 days
  • JWT secrets: every 90 days (requires all existing sessions to re-authenticate)
  • Third-party API keys: every 180 days, or immediately after any team member leaves
  • CI/CD secrets: when the team member who set them up leaves

Doppler and AWS Secrets Manager support automated rotation for some secret types (database passwords via RDS, for example).

Keep Reading


Pristren builds AI-powered software for teams. Zlyqor is our all-in-one workspace - chat, projects, time tracking, AI meeting summaries, and invoicing - in one tool. Try it free.

#secrets-management#security#api-keys#developer-tools#devops

// discussion

Comments

0/4000
Mahmudul Haque Qudrati — CEO & ML Engineer at Pristren

Mahmudul Haque Qudrati

CEO & ML Engineer

Visionary technologist, software engineer, and machine learning specialist. Founder and CEO of Pristren, directing engineering teams that ship production-grade AI/ML pipelines, mission-critical full-stack applications, and developer tooling. Creator of Zlyqor, the unified team workspace platform. Author of 540+ technical guides and benchmark research reports on large language models, agentic workflows, Model Context Protocol (MCP), and modern web stacks.

PristrenZlyqor