Tips & Tricks (Updated: 6/2/2026)

Claude Code Secrets Management: From .env to Production Rotation

Guide to .env, CI/CD secrets, cloud stores, redacted logs, and safe Claude Code boundaries.

Claude Code Secrets Management: From .env to Production Rotation

API keys, database URLs, OAuth client secrets, and cloud deployment credentials are required for real applications. They are also the values that keep surviving in Git history, CI logs, screenshots, tickets, and draft articles after one careless paste. When you use Claude Code for debugging, migration, documentation, or deployment work, the first rule is simple: the assistant can help build the safety system, but it should not see, print, store, or reuse the secret values themselves.

Secrets management is not just a password vault. It is the operating model for keeping configuration outside code, separating local, development, staging, and production values, limiting permissions, validating values before the app starts, redacting logs, and rotating keys before an incident becomes a business problem. The Twelve-Factor App config principle is still the right starting point: configuration belongs in the environment, not hard-coded into the repository. In practice, you also need .gitignore, .env.example, a validated config loader, CI/CD secrets, a cloud secret store, and a rotation checklist.

flowchart LR
  Dev["Local .env"] --> Loader["Validated config loader"]
  CI["CI/CD secrets"] --> Deploy["Deployment runtime"]
  Store["AWS / GCP / Azure secret store"] --> Deploy
  Deploy --> App["Application process"]
  App --> Logs["Redacted logs"]

1. Decide what belongs where

The beginner mistake is treating every configuration value the same. A useful test is this: if a leaked value lets someone spend money, send email, read data, write data, deploy code, or impersonate a user, treat it as a secret. Public base URLs, feature flag names, and OAuth client IDs usually need less protection because they do not grant authority by themselves.

Use caseExamplesLocal developmentProduction
Email and API integrationsSendGrid API key, Stripe secret key, GitHub tokenUse sandbox or test keys in .envInject from CI/CD secrets or a secret store
Database accessDATABASE_URL, user, passwordUse a local or dev-only database userSplit read/write privileges and rotate passwords
Cloud deploymentAWS/GCP/Azure credentialsAvoid long-lived local cloud keysUse OIDC or a deploy-only role
OAuth and webhooksOAUTH_CLIENT_SECRET, webhook signing secretCreate a local app registrationStore prod-only secrets outside the repo

The important point is separation. Do not reuse the production Stripe key locally. Do not use a personal GitHub token with broad organization access in CI. Do not give a deployment key permission to administer billing or users. This is least privilege: grant only the scope needed for the task, only in the environment where it is needed, and preferably for a limited lifetime.

2. Commit the shape, never the value

.env is useful for local work, but it should never be committed. The repository should contain .env.example, which documents the required variable names, value formats, and source systems. Pair this article with the environment management guide when onboarding new developers so they know which values they must create themselves.

# Local secrets
.env
.env.*
!.env.example
!.env.test.example

# Logs and generated output that may contain tokens
npm-debug.log*
yarn-debug.log*
coverage/
dist/
# .env.example - placeholders only, never real values
NODE_ENV=development
APP_BASE_URL=http://localhost:3000

# Use a local database user, not production credentials.
DATABASE_URL=postgres://app_user:replace-me@localhost:5432/app_dev

# Use test/sandbox keys for local development.
SENDGRID_API_KEY=SG.xxxxxx
STRIPE_SECRET_KEY=sk_test_xxxxxx
GITHUB_TOKEN=ghp_xxxxxx

# OAuth secrets must be separated by environment.
OAUTH_CLIENT_ID=local-client-id
OAUTH_CLIENT_SECRET=replace-me

# Deployment reads these from CI or a cloud secret store.
AWS_REGION=ap-northeast-1
DEPLOY_ROLE_ARN=arn:aws:iam::123456789012:role/app-deploy-dev

When you involve Claude Code, make the boundary explicit: it may read .env.example, deployment manifests, package scripts, and variable names; it may not open .env, paste values into chat, or write real credentials into docs. If the implementation needs a real value, you set it outside the prompt and tell Claude Code only the variable name that should be wired.

3. Validate configuration and redact logs

Applications should fail fast when a required secret is missing or malformed. They should also make it hard for a developer to accidentally print a token during debugging. The following Node.js loader uses dotenv and envalid to validate the environment at startup and provides a redaction helper for logs, health checks, and support output.

import { config as loadDotenv } from "dotenv";
import { cleanEnv, str, url } from "envalid";

const envFile = process.env.NODE_ENV === "test" ? ".env.test" : ".env";
loadDotenv({ path: envFile });

const secretKeyPattern = /(KEY|TOKEN|SECRET|PASSWORD|DATABASE_URL|PRIVATE)/i;

const env = cleanEnv(process.env, {
  NODE_ENV: str({
    choices: ["development", "test", "staging", "production"],
    default: "development",
  }),
  APP_BASE_URL: url({ default: "http://localhost:3000" }),
  DATABASE_URL: url(),
  SENDGRID_API_KEY: str(),
  STRIPE_SECRET_KEY: str(),
  GITHUB_TOKEN: str(),
  OAUTH_CLIENT_ID: str(),
  OAUTH_CLIENT_SECRET: str(),
  AWS_REGION: str({ default: "ap-northeast-1" }),
  DEPLOY_ROLE_ARN: str(),
});

export function appConfig() {
  return Object.freeze({
    nodeEnv: env.NODE_ENV,
    appBaseUrl: env.APP_BASE_URL,
    databaseUrl: env.DATABASE_URL,
    sendgridApiKey: env.SENDGRID_API_KEY,
    stripeSecretKey: env.STRIPE_SECRET_KEY,
    githubToken: env.GITHUB_TOKEN,
    oauthClientId: env.OAUTH_CLIENT_ID,
    oauthClientSecret: env.OAUTH_CLIENT_SECRET,
    awsRegion: env.AWS_REGION,
    deployRoleArn: env.DEPLOY_ROLE_ARN,
  });
}

export function redactValue(key, value) {
  if (!secretKeyPattern.test(key)) return value;
  if (!value) return "<empty>";
  const text = String(value);
  if (text.length <= 8) return "<redacted>";
  return `${text.slice(0, 4)}...${text.slice(-4)}`;
}

export function redactConfig(config) {
  return Object.fromEntries(
    Object.entries(config).map(([key, value]) => [key, redactValue(key, value)]),
  );
}

if (process.argv[1] === new URL(import.meta.url).pathname) {
  console.log(redactConfig(appConfig()));
}

A common failure is adding console.log(process.env) during a late-night incident and then pasting the CI log into a ticket, Slack thread, or article draft. Claude Code can accidentally amplify that mistake if you ask it to summarize raw logs. Redact first, then ask for help. The same rule applies to screenshots: crop or blur tokens before uploading them anywhere, including prompts.

4. Give Claude Code explicit permission boundaries

Claude Code rarely needs secret values. It needs names, formats, error messages with values removed, and the intent of each permission. Put the boundary in the first prompt for any security, deployment, or debugging task.

You may inspect .env.example, package.json, deployment manifests, and secret names.
Do not open, print, summarize, store, or copy .env, CI/CD secret values, cloud credentials, production dumps, or screenshots containing tokens.
When you need a value, ask me to set it outside chat and confirm only the variable name.
If a secret appears in command output, stop, redact it, and report which file or command exposed it.
Before changing permissions, explain the least-privilege scope and ask for approval.
Do not paste real secrets into prompts, logs, documentation, code comments, tests, tickets, or articles.

Also constrain commands. Treat printenv, cat .env, cloud CLI commands that reveal credentials, full CI log dumps, and screenshot capture as actions that require confirmation. Most fixes can be completed from .env.example, typed config, masked errors, and official documentation. If Claude Code generates a README, test fixture, or blog example, review it for accidental real keys before publishing.

5. Use CI/CD secrets and cloud secret stores deliberately

A practical split is .env for local development, CI/CD secrets for pipelines, and a cloud secret store for production runtime. On AWS, use AWS Secrets Manager. On Google Cloud, use Secret Manager. On Azure, use Key Vault. For GitHub repositories, enable secret scanning so leaked tokens are detected quickly.

name: deploy

on:
  workflow_dispatch:

jobs:
  deploy:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      id-token: write
    env:
      NODE_ENV: production
      AWS_REGION: ap-northeast-1
    steps:
      - uses: actions/checkout@v4
      - name: Validate required secret names
        run: test -n "${{ secrets.DEPLOY_ROLE_ARN }}" && test -n "${{ secrets.DATABASE_URL }}"
      - name: Deploy without echoing secrets
        env:
          DATABASE_URL: ${{ secrets.DATABASE_URL }}
          DEPLOY_ROLE_ARN: ${{ secrets.DEPLOY_ROLE_ARN }}
        run: npm run deploy

The trap in CI is “just echo it to see what is happening.” GitHub Actions masks many exact secret values, but it cannot reliably protect derived strings, encoded URLs, JSON blobs, copied screenshots, or values that were never registered as secrets. Ask Claude Code to show the variable names and validation steps, not the values. If it proposes contents: write, actions: write, or broad cloud permissions, require a reason and downgrade the scope where possible.

6. Rotate before you are forced to

Rotation should be a normal maintenance task, not only an emergency response. API keys for SendGrid, Stripe, and GitHub, database passwords, OAuth client secrets, webhook signing secrets, and cloud trust policies should have an owner, environment, consumer list, last rotation date, and next review date.

## Secret rotation checklist
- [ ] Identify owner, environment, consumers, and business impact.
- [ ] Create a new secret with the smallest required scope.
- [ ] Store it in CI/CD secrets or the cloud secret store.
- [ ] Deploy one service or job with the new value.
- [ ] Confirm logs and metrics without printing the secret.
- [ ] Revoke the old secret.
- [ ] Scan Git history, tickets, docs, screenshots, and chat snippets.
- [ ] Record the rotation date and next review date.

Claude Code is useful for turning this into an issue template, runbook, or migration checklist. It should not receive the old or new secret value. Give it a task like, “Find all references to SENDGRID_API_KEY and prepare a safe rollout plan for the v2 key.” Keep the actual replacement in the provider dashboard, CI settings, or secret store UI.

7. Review the failure cases before release

Block release or publication if any of these are true:

  • .env or .env.production was committed, and the exposed key has not been revoked.
  • Screenshots, CI logs, error reports, or article examples contain tokens, database URLs, or OAuth secrets.
  • A cloud key has broad administrator permissions when deploy-only access would work.
  • Production Stripe, SendGrid, database, or GitHub credentials are reused in local development.
  • OAuth client secrets or webhook signing secrets have no rotation owner or runbook.
  • An AI assistant was given real values and then reused them in README files, tests, tickets, or drafts.

Security incidents usually come from process gaps as much as code. Use the security audit checklist to review permissions, the security failure cases guide to train reviewers, the API development guide for service boundaries, and the email automation article when handling SendGrid or transactional mail.

8. Roll this out team by team

Do not start by migrating every service to a cloud secret store in one week. Pick one application and make the whole path work: .gitignore, .env.example, config loader, redacted logging, CI/CD secrets, cloud storage for production, and a rotation record. Then move the recurring secret types in order: SendGrid, Stripe, GitHub tokens, DATABASE_URL, OAuth client secrets, and deploy credentials.

ClaudeCodeLab training and consulting sessions use your real repository shape, but not your real secret values. We map which files Claude Code may inspect, design CI/CD secret boundaries, enable GitHub secret scanning, plan AWS/GCP/Azure secret-store migration, and leave your team with reusable prompts and runbooks. That is more useful than a generic lecture because reviewers can apply the template the next day.

In short: secrets management is not only about hiding strings. It is about separating values from code, isolating environments, validating startup config, redacting logs, narrowing permissions, and practicing rotation. Claude Code can help build and review that system, but it should not become another place where secrets are copied.

After testing the workflow in Masa’s sample Node.js app, the config loader caught missing values, redactConfig kept the database URL and API keys out of CI logs, and the deploy workflow no longer needed broad write permissions. The only surprise was an older screenshot that still showed a Stripe test key, so the key was reissued before publication. The practical lesson: review logs, screenshots, and drafts with the same suspicion as source code.

#claude-code #security #secrets-management #devops
Free

Free PDF: Claude Code Cheatsheet

Enter your email and download the one-page Claude Code cheatsheet for commands, review habits, and safe workflows.

We handle your data with care and never send spam.

Level up your Claude Code workflow

Start with the free PDF, use Gumroad guides when you need repeatable workflows, and book consultation when rollout or revenue paths need human judgment.

Masa

About the Author

Masa

Engineer focused on practical Claude Code workflows. Runs claudecode-lab.com, a 10-language technical media site.