Claude Code Environment Management: .env, Zod, Secrets und sichere Deployments
Umgebungsvariablen und Secrets mit Claude Code verwalten: .env.example, Zod-Validierung, CI/CD, Redaction und Rotation.
Wenn Einsteiger Claude Code bitten, Login, Payments, Webhooks oder eine KI-Integration zu bauen, liegt der erste ernste Produktionsfehler oft nicht im UI. Er liegt in der Konfiguration: eine fehlende Datenbank-URL, ein Staging-Key in Production, ein WEBHOOK_SECRET in CI-Logs oder ein echter API-Key in GitHub.
Dieser Leitfaden zeigt ein direkt nutzbares Muster für Umgebungsvariablen und Secrets mit Claude Code. Eine Umgebungsvariable ist ein Wert, der der App zur Laufzeit übergeben wird, zum Beispiel PORT oder APP_ORIGIN. Ein Secret ist ein Wert, der bei Offenlegung missbraucht werden kann, etwa ANTHROPIC_API_KEY, DATABASE_URL oder WEBHOOK_SECRET. Secrets können technisch als Umgebungsvariablen ankommen, brauchen aber strengere Regeln.
Für Claude Codes eigene Konfiguration ist die offizielle Referenz Claude Code environment variables maßgeblich. Für die App-Validierung nutzen wir Zod. Für CI/CD und Deployments sind GitHub Actions secrets, Vercel environment variables, Cloudflare Workers variables and secrets und Docker secrets die relevanten offiziellen Quellen. Die Plattformen unterscheiden sich, aber das Prinzip bleibt gleich: Namen und Regeln versionieren, nie die Secret-Werte.
Für die breitere Sicherheitsbasis passen dazu Claude Code Security Best Practices und JWT-Authentifizierung mit Claude Code.
.env als Vertrag behandeln
Eine .env-Datei ist praktisch, darf aber nicht zum privaten Notizblock jedes Entwicklers werden. Ein Team braucht drei Schichten:
- Deklaration:
.env.examplelistet die benötigten Keys - Validierung: Zod bricht beim Start ab, wenn Werte fehlen oder ungültig sind
- Betrieb: CI/CD und Production injizieren Werte aus Plattform-Secrets
flowchart LR
Dev["local .env.local"] --> Schema["Zod schema"]
CI["GitHub Actions secrets"] --> Schema
Prod["Vercel / Cloudflare / Docker secrets"] --> Schema
Schema --> App["Type-safe app config"]
Schema --> Logs["Redacted logs"]
Example[".env.example"] --> Dev
Der Kern: Alle Eingänge laufen durch dasselbe Schema. Claude Code bekommt Key-Namen, Validierungsregeln und Fehlerverhalten, aber keine echten Produktionswerte.
Konkrete Einsatzfälle
| Einsatzfall | Typische Variablen | Fehlerbild |
|---|---|---|
| Lokale Entwicklung | APP_ORIGIN, DATABASE_URL, ANTHROPIC_API_KEY | Die App läuft nur auf einem Rechner |
| Webhook-Prüfung | STRIPE_WEBHOOK_SECRET, WEBHOOK_SECRET | Gefälschte Requests werden akzeptiert |
| CI-Tests | CI_DATABASE_URL, TEST_API_KEY | PR grün, Deployment rot |
| Production | DATABASE_URL, SESSION_SECRET, APP_ORIGIN | Falsche DB, Cookie-Probleme, Credential-Leak |
| Secret-Rotation | ANTHROPIC_API_KEY_NEXT | Ein kompromittierter alter Key bleibt gültig |
In Masas ClaudeCodeLab-Workflow war der größte Hebel nicht nur eine .env.example, sondern ein Startabbruch bei unvollständiger Konfiguration. So werden Deployment-Überraschungen zu überprüfbaren PR-Fehlern.
1. Dateien sauber trennen
.env.example ist Dokumentation, keine Ablage für echte Werte. .env.local gehört zu einer Maschine. .env.production.example ist eine Production-Checkliste ohne Secrets.
mkdir -p src/config
touch .env.example .env.local .env.production.example src/config/env.ts
# .gitignore
.env
.env.*
!.env.example
!.env.production.example
# Cloudflare local secrets
.dev.vars
.dev.vars.*
# .env.example
APP_ENV=local
NODE_ENV=development
PORT=3000
APP_ORIGIN=http://localhost:3000
DATABASE_URL=postgresql://app:app@localhost:5432/app
ANTHROPIC_API_KEY=replace-with-local-dev-key
WEBHOOK_SECRET=replace-with-32-plus-character-secret
PUBLIC_ANALYTICS_KEY=
LOG_LEVEL=info
# .env.production.example
APP_ENV=production
NODE_ENV=production
PORT=3000
APP_ORIGIN=https://example.com
DATABASE_URL=<set-in-platform-secret-store>
ANTHROPIC_API_KEY=<set-in-platform-secret-store>
WEBHOOK_SECRET=<set-in-platform-secret-store>
PUBLIC_ANALYTICS_KEY=<optional-public-key>
LOG_LEVEL=info
Die Platzhalter sind keine sicheren Defaults. Sie markieren nur, dass an anderer Stelle ein Wert gesetzt werden muss.
2. Beim Start mit Zod validieren
In Node.js sind Umgebungsvariablen zunächst Strings. Auch PORT=3000 ist ein String, deshalb konvertiert und prüft z.coerce.number() den Wert.
npm install zod dotenv
npm install -D tsx typescript @types/node
// src/config/env.ts
import "dotenv/config";
import { z } from "zod";
const secretNamePattern = /(SECRET|TOKEN|PASSWORD|API_KEY|DATABASE_URL|DSN)/i;
function redactValue(key: string, value: unknown): string {
if (value === undefined || value === null || value === "") return "<empty>";
const text = String(value);
if (!secretNamePattern.test(key)) return text;
if (text.length <= 8) return "<redacted>";
return `${text.slice(0, 4)}...${text.slice(-4)}`;
}
const envSchema = z.object({
APP_ENV: z.enum(["local", "development", "staging", "production"]).default("local"),
NODE_ENV: z.enum(["development", "test", "production"]).default("development"),
PORT: z.coerce.number().int().min(1).max(65535).default(3000),
APP_ORIGIN: z.string().url(),
DATABASE_URL: z.string().url(),
ANTHROPIC_API_KEY: z.string().min(20, "ANTHROPIC_API_KEY is too short"),
WEBHOOK_SECRET: z.string().min(32, "WEBHOOK_SECRET must be at least 32 characters"),
PUBLIC_ANALYTICS_KEY: z.string().optional(),
LOG_LEVEL: z.enum(["debug", "info", "warn", "error"]).default("info"),
});
const parsed = envSchema.safeParse(process.env);
if (!parsed.success) {
console.error("Environment validation failed:");
for (const issue of parsed.error.issues) {
const key = String(issue.path[0] ?? "unknown");
console.error(`- ${key}: ${issue.message}; current=${redactValue(key, process.env[key])}`);
}
process.exit(1);
}
export const env = Object.freeze(parsed.data);
export type AppEnv = typeof env;
export function isProduction(): boolean {
return env.APP_ENV === "production";
}
export function publicEnv() {
return {
APP_ENV: env.APP_ENV,
APP_ORIGIN: env.APP_ORIGIN,
PUBLIC_ANALYTICS_KEY: env.PUBLIC_ANALYTICS_KEY ?? "",
};
}
Lokal prüfen:
cp .env.example .env.local
npx tsx src/config/env.ts
Danach kann Claude Code verstreute Zugriffe finden:
Finde alle direkten process.env-Zugriffe in diesem Repository.
Nur src/config/env.ts soll process.env direkt lesen.
Für andere Dateien schlage vor, env aus src/config/env.ts zu importieren.
Secrets dürfen nicht in Logs, Fehlern oder Test-Snapshots erscheinen.
3. Vor dem Logging redigieren
Secret-Leaks entstehen nicht nur durch Git. Sie tauchen in CI-Logs, Debug-Ausgaben, Error-Tracking, Bildschirmaufnahmen oder in Claude-Code-Prompts auf.
// src/config/redact.ts
const sensitiveKeyPattern = /(SECRET|TOKEN|PASSWORD|API_KEY|DATABASE_URL|AUTH|COOKIE|PRIVATE)/i;
export function redactSecrets(input: Record<string, unknown>): Record<string, string> {
return Object.fromEntries(
Object.entries(input).map(([key, value]) => {
if (value === undefined || value === null || value === "") return [key, "<empty>"];
const text = String(value);
if (!sensitiveKeyPattern.test(key)) return [key, text];
return [key, text.length <= 10 ? "<redacted>" : `${text.slice(0, 4)}...${text.slice(-4)}`];
}),
);
}
import { env } from "./env";
import { redactSecrets } from "./redact";
console.info("Loaded config", redactSecrets(env));
Redaction ist die letzte Sicherung. Noch besser ist ein Log, das den Secret-Wert nie erhält.
4. Werte über CI/CD injizieren
GitHub Actions kann Secrets auf Repository-, Environment- oder Organization-Ebene übergeben. Verwenden Sie für normale PR-Tests keine Production-Credentials, sondern eingeschränkte CI-Werte.
# .github/workflows/env-check.yml
name: env-check
on:
pull_request:
push:
branches: [main]
jobs:
validate-env:
runs-on: ubuntu-latest
env:
APP_ENV: development
NODE_ENV: test
PORT: 3000
APP_ORIGIN: http://localhost:3000
DATABASE_URL: ${{ secrets.CI_DATABASE_URL }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
WEBHOOK_SECRET: ${{ secrets.WEBHOOK_SECRET }}
LOG_LEVEL: info
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "22"
cache: npm
- run: npm ci
- name: Mask runtime-only values
run: echo "::add-mask::$APP_ORIGIN"
- run: npx tsx src/config/env.ts
- run: npm test -- --runInBand
Nicht jeder Workflow erhält Secrets unter denselben Bedingungen. Fork-PRs, wiederverwendbare Workflows und automatisierte Events können eingeschränkt sein. Halten Sie die Validierung explizit und schreiben Sie Secrets nicht in generierte Dateien.
5. Docker, Vercel und Cloudflare
In Docker sollten echte Secrets nicht mit ENV API_KEY=... im Dockerfile landen. Lokal ist ein env file in Ordnung; in Production sollte die Runtime oder der Secret Store injizieren.
# local only
docker run --rm --env-file .env.local my-app:latest
Wenn die Runtime Secrets als Dateien bereitstellt, unterstützt die NAME_FILE-Konvention:
// src/config/secret-file.ts
import fs from "node:fs";
export function readEnvOrFile(name: string): string | undefined {
const direct = process.env[name];
if (direct) return direct;
const filePath = process.env[`${name}_FILE`];
if (!filePath) return undefined;
return fs.readFileSync(filePath, "utf8").trim();
}
Auf Vercel trennen Sie Production, Preview und Development. Achten Sie besonders auf im Browser sichtbare Präfixe wie NEXT_PUBLIC_. In Cloudflare Workers kommen Werte je nach Setup über Bindings, den env-Parameter oder Plattform-Secrets. Nicht auf magische Portabilität vertrauen: Schema als Quelle der Wahrheit halten und die Injektionsstelle je Plattform dokumentieren.
Prüfe die Environment-Konfiguration für Vercel, Cloudflare und Docker.
Lies oder fordere keine echten Production-Werte an.
Prüfe Pflicht-Keys, public vs secret, build-time vs runtime und fehlende Rotationsnotizen.
6. Rotations-Playbook vorher schreiben
Secret-Rotation darf nicht erst im Incident entstehen:
- Scope klären: Service, Umgebung, Berechtigungen, Owner
- Neuen Wert mit minimalen Berechtigungen erstellen
- Wenn möglich als
*_NEXThinzufügen - App für kurze Zeit alt und neu akzeptieren lassen
- Deployen und Health Checks prüfen
- Alten Wert widerrufen
- Git-Historie, CI-Logs, Monitoring-Logs und Prompts durchsuchen
.env.exampleund Betriebsnotizen aktualisieren
Webhook-Secret, API-Key und DB-Passwort rotieren unterschiedlich. Halten Sie Owner, Zeitfenster und Rollback pro Typ fest.
Häufige Fehler
| Fehler | Ursache | Gegenmaßnahme |
|---|---|---|
.env committet | .gitignore kam zu spät | Keys sofort widerrufen; Git-Bereinigung reicht nicht |
Secret in NEXT_PUBLIC_ | Öffentliches Präfix missverstanden | Public/private Namensregeln trennen |
console.log(process.env) | Eilige Fehlersuche | Redaction und Log-Review |
| Production startet nicht | Pflicht-Key fehlt | src/config/env.ts in CI ausführen |
| Lokaler Wert im Production-Build | Build-time/runtime verwechselt | Plattform-Injektion dokumentieren |
| Echter Key in Claude Code eingefügt | Prompting mit Secret-Sharing verwechselt | Nur Namen und Regeln teilen |
Direkt nutzbarer Claude-Code-Prompt
Implementiere Environment-Variable-Management für dieses Projekt.
Anforderungen:
- .env.example und .env.production.example erstellen
- .env, .env.* und .dev.vars* aus Git ausschließen
- Zod-Schema in src/config/env.ts hinzufügen und bei fehlenden/ungültigen Werten abbrechen
- Direkte process.env-Zugriffe in src/config/env.ts zentralisieren
- Secrets in Diagnose-Logs redigieren
- GitHub-Actions-Job hinzufügen, der Env-Validierung in Pull Requests ausführt
- Kurze Deployment-Notizen für Vercel, Cloudflare und Docker schreiben
Lies keine echten API-Keys oder Production-Datenbank-URLs. Arbeite nur mit Key-Namen und Validierungsregeln.
Fazit
Gutes Environment Management mit Claude Code bedeutet nicht, Secrets in den Chat zu kopieren. Es bedeutet, einen prüfbaren Vertrag zu implementieren: .env.example deklariert Keys, Zod validiert beim Start, Logs werden redigiert, CI/CD und Deployment-Plattformen injizieren echte Werte, und die Rotation ist vorbereitet.
ClaudeCodeLab bietet Claude-Code-Beratung, Team-Training, Repository-Sicherheitsreviews und Templates für Authentifizierung, Payments, CI/CD und Content Operations. Wer mit Claude Code schneller werden will, ohne Production-Keys zu verlieren, sollte diese Grundlage früh standardisieren.
In Masas Test-Repository hat dieses Muster vor dem Deployment drei Probleme gefunden: fehlender Production-Key, mögliches Logging eines Webhook-Secrets und eine veraltete .env.example. Die Zod-Startvalidierung ist einfach, macht Konfiguration aber zu einem durchsetzbaren Teamvertrag.
Kostenloses PDF: Claude-Code-Cheatsheet
E-Mail eintragen und eine Seite mit Befehlen, Review-Gewohnheiten und sicheren Workflows herunterladen.
Wir schützen Ihre Daten und senden keinen Spam.
Über den Autor
Masa
Engineer für praktische Claude-Code-Workflows und Team-Einführung.
Ähnliche Artikel
Claude Code Permission Safety Ladder: Zugriff kontrolliert erweitern
Von read-only zu begrenzten Änderungen, Prüfbefehlen und Deploy-Checks mit klarer Kontrolle.
Claude Code Small PR Proof Pack: kleine Änderungen reviewbar machen
Ein Proof Pack für Claude-Code-PRs: Diff, Checks, öffentliche URL, CTA-Pfad und Rollback.
Claude-Code-Review-Gate vor dem Commit
Vor dem Commit mit Claude Code prüfen: Diff, Build, öffentliche URL, Gumroad-Links, Beratung-CTA, fehlende Tests und fremde Dateien.