Skip to main content
DevOps7 min readMarch 3, 2026

Secrets Management: Keeping Credentials Out of Your Codebase

A practical guide to secrets management for development teams — vault solutions, secret injection patterns, rotation automation, and audit trails for production credentials.

James Ross Jr.

James Ross Jr.

Strategic Systems Architect & Enterprise Software Developer

Secrets Management: Keeping Credentials Out of Your Codebase

In 2026, there are still thousands of GitHub repositories with database passwords, API keys, and AWS access credentials committed in their history. Search GitHub for "remove password" in commit messages and you will find evidence of the cleanup that happens after these mistakes. But cleanup is not sufficient — once a secret is in your git history, it must be treated as compromised. Rotating it is mandatory.

The good news is that properly managed secrets are not complicated. The tools are mature, the patterns are well-established, and the operational overhead is minimal once you set it up correctly. Here is the complete guide.

The Hard Rule: Secrets Never Touch Your Repository

I want to start with the absolute rule before getting into tooling: secrets never appear in your repository. Not in code, not in configuration files, not in comments, not in commit messages, not in .env files that accidentally do not get gitignored. Not once, not even in a commit you plan to immediately revert.

The reason is git history is permanent. Reverting a commit does not remove it from history. Squashing commits does not remove it. Force-pushing to remove history can corrupt remote repository state and requires notifying everyone who has cloned the repository that they need to re-clone. Even after all that, the secret may have been indexed by GitHub's search, scraped by a bot, or cached somewhere.

The operationally correct response when a secret is committed is: assume it compromised, rotate it immediately, then clean the history. In that order. Rotate first because the rotation is urgent. History cleanup can happen after the immediate risk is addressed.

Detecting Secrets Before They Are Committed

Pre-commit hooks can catch secrets before they enter your repository. git-secrets and detect-secrets are two tools designed for this:

# Install detect-secrets
pip install detect-secrets

# Initialize a baseline (mark known false positives as allowed)
detect-secrets scan > .secrets.baseline

# Install the pre-commit hook
detect-secrets-hook --baseline .secrets.baseline

Or using the pre-commit framework with multiple hooks:

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/Yelp/detect-secrets
    rev: v1.4.0
    hooks:
      - id: detect-secrets
        args: ["--baseline", ".secrets.baseline"]

  - repo: https://github.com/awslabs/git-secrets
    rev: 1.3.0
    hooks:
      - id: git-secrets

In CI, run the same scan on every PR:

- name: Scan for secrets
  uses: trufflesecurity/trufflehog@main
  with:
    path: ./
    base: ${{ github.event.repository.default_branch }}
    head: HEAD

TruffleHog is excellent for CI scanning — it scans the diff of a PR against the base branch and reports any high-entropy strings or credential patterns it finds.

Secrets Management Tools by Scale

For local development (any team size): Doppler

Doppler centralizes your secrets and injects them into running processes. Developers install the Doppler CLI, authenticate, and run doppler run -- npm run dev. Doppler injects the configured environment variables at process startup. No .env files, no manual credential sharing through Slack.

The free tier covers up to five projects and unlimited users. The UI is clean, the CLI is fast, and the integration with all major platforms (GitHub Actions, Vercel, Railway, Kubernetes) is excellent.

For AWS-centric infrastructure: AWS Secrets Manager

AWS Secrets Manager stores secrets with automatic rotation, cross-region replication, and fine-grained IAM access control. Secrets are accessed via API — your application retrieves them at startup rather than having them injected as environment variables.

import { SecretsManagerClient, GetSecretValueCommand } from "@aws-sdk/client-secrets-manager";

async function getSecret(secretName: string): Promise<string> {
  const client = new SecretsManagerClient({ region: "us-east-1" });
  const response = await client.send(
    new GetSecretValueCommand({ SecretId: secretName })
  );
  return response.SecretString ?? "";
}

// At application startup
const dbPassword = await getSecret("production/api/database-password");

The advantage over environment variables is that secrets can be rotated without restarting the application if you re-fetch them periodically. The disadvantage is coupling your application to AWS SDK for configuration.

For Kubernetes: External Secrets Operator

The External Secrets Operator synchronizes secrets from external secret management systems (AWS Secrets Manager, HashiCorp Vault, GCP Secret Manager) into Kubernetes Secrets. Your application references Kubernetes Secrets as normal — no external SDK calls, no cloud provider coupling:

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: api-secrets
  namespace: production
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: aws-secrets-manager
    kind: SecretStore
  target:
    name: api-secrets
    creationPolicy: Owner
  data:
    - secretKey: database-url
      remoteRef:
        key: production/api/database-url

This creates a Kubernetes Secret named api-secrets that is refreshed hourly from AWS Secrets Manager. When the secret rotates in Secrets Manager, the Kubernetes Secret updates automatically.

For self-hosted or multi-cloud: HashiCorp Vault

Vault is the most comprehensive solution: dynamic secrets (generate credentials on demand, automatically revoke them when done), detailed audit logging, fine-grained access control policies, and support for dozens of secret backends. It is also significantly more complex to operate.

For teams with the operational capacity, Vault is the gold standard. For most small to mid-sized teams, Doppler or AWS Secrets Manager provides 90% of the value with a fraction of the operational overhead.

Secret Injection Patterns

The three patterns for getting secrets into your application:

Environment variable injection — your secrets manager injects secrets as environment variables at process startup. Simple, universal, but secrets are visible to any process in the same environment and are captured in crash dumps.

File mounting — secrets are written to a file at a well-known path. The application reads the file. Files can have strict permissions (readable only by the application user). Kubernetes Secrets can be mounted as files using volume mounts.

Direct API access — the application calls the secrets manager API to retrieve secrets at startup. The most flexible and most complex. Appropriate when you need dynamic secrets or per-request credential generation.

For most applications, environment variable injection is the pragmatic choice. It works with every framework, requires no application code changes, and is supported by every secrets management tool.

Audit Trails

Know who accessed what secret when. Every secrets management tool provides access logging. Review it.

AWS Secrets Manager access is logged in CloudTrail. Enable CloudTrail if you have not — it logs all API calls in your AWS account. Set up an alert for unusual access patterns: a secret being accessed from a new IP, a secret being accessed at unusual times, or a secret being accessed by an unexpected IAM principal.

Doppler provides an access audit log per secret. When a secret is retrieved or updated, it is recorded with the accessor and timestamp.

Audit logs are useful in two scenarios: detecting unauthorized access to secrets (security monitoring) and attributing changes when something breaks (troubleshooting "who changed the database password?").

The Secrets Hygiene Checklist

Every production application should satisfy:

  • No secrets in the git repository or history
  • Secrets scanning in pre-commit hooks and CI
  • All production secrets stored in a dedicated secrets manager
  • Secrets scoped to the minimum necessary access (the database password for the API does not need admin privileges)
  • Access to production secrets limited to production systems and authorized team members
  • Audit logging enabled and reviewed
  • Rotation schedule defined for each secret
  • Automated rotation configured where the secret manager and service support it

This is not a complex or expensive checklist. The tooling exists, most of it has free tiers, and the setup time is measured in hours, not days.


If you need help implementing secrets management for your team or want to audit your current credential handling practices, book a session at https://calendly.com/jamesrossjr.


Keep Reading