Environment Variable di Claude Code: .env, Zod, Secrets, dan Deploy Aman
Kelola environment variable dan Secrets dengan Claude Code: .env.example, validasi Zod, CI/CD, redaction, dan rotasi.
Saat pemula meminta Claude Code membuat login, pembayaran, webhook, atau integrasi AI, bug production pertama sering bukan berasal dari UI. Masalah biasanya muncul di konfigurasi: URL database kurang, key staging dipakai di production, WEBHOOK_SECRET tercetak di log CI, atau API key asli ikut ter-commit ke GitHub.
Panduan ini memberikan pola yang siap diimplementasikan untuk mengelola environment variable dan Secrets dengan Claude Code. Environment variable adalah nilai yang diberikan ke aplikasi saat runtime, seperti PORT atau APP_ORIGIN. Secret adalah nilai yang bisa disalahgunakan jika bocor, seperti ANTHROPIC_API_KEY, DATABASE_URL, atau WEBHOOK_SECRET. Secret sering dikirim lewat environment variable, tetapi perlakuannya harus lebih ketat.
Untuk konfigurasi Claude Code sendiri, gunakan referensi resmi Claude Code environment variables. Untuk validasi konfigurasi aplikasi, kita memakai Zod. Untuk CI/CD dan deploy, rujuk GitHub Actions secrets, Vercel environment variables, Cloudflare Workers variables and secrets, dan Docker secrets. Platform bisa berbeda, tetapi prinsipnya sama: simpan nama key dan aturan validasi di code, bukan nilai secret.
Untuk konteks keamanan yang lebih luas, baca juga praktik keamanan Claude Code dan JWT authentication dengan Claude Code.
Perlakukan .env sebagai kontrak
File .env sangat berguna, tetapi jangan biarkan menjadi catatan pribadi tiap developer. Tim membutuhkan tiga lapis kontrol:
- Deklarasi:
.env.examplemencatat key yang wajib ada - Validasi: Zod menggagalkan startup jika nilai hilang atau format salah
- Operasi: CI/CD dan production menginjeksi nilai dari Secrets platform
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
Intinya, semua entry point lewat schema yang sama. Claude Code perlu menerima nama key, aturan validasi, dan perilaku saat gagal. Jangan berikan nilai production asli di prompt.
Use case konkret
| Use case | Nilai umum | Mode gagal |
|---|---|---|
| Development lokal | APP_ORIGIN, DATABASE_URL, ANTHROPIC_API_KEY | Aplikasi hanya jalan di laptop satu orang |
| Verifikasi webhook | STRIPE_WEBHOOK_SECRET, WEBHOOK_SECRET | Request palsu diterima |
| Testing CI | CI_DATABASE_URL, TEST_API_KEY | PR lolos, tetapi deploy gagal |
| Deploy production | DATABASE_URL, SESSION_SECRET, APP_ORIGIN | Salah DB, cookie rusak, credential bocor |
| Rotasi secret | ANTHROPIC_API_KEY_NEXT | Key lama yang bocor tetap valid terlalu lama |
Dalam workflow Masa di ClaudeCodeLab, peningkatan terbesar bukan hanya membuat .env.example, tetapi membuat aplikasi menolak startup saat kontrak konfigurasi belum lengkap. Masalah yang biasanya muncul saat deploy berubah menjadi failure yang bisa direview di pull request.
1. Pisahkan file lebih dulu
.env.example adalah dokumentasi, bukan tempat nilai asli. .env.local hanya untuk mesin lokal. .env.production.example adalah checklist production tanpa nilai secret.
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
Placeholder bukan default yang aman. Placeholder hanya menandakan bahwa nilai harus disediakan dari tempat lain.
2. Validasi saat startup dengan Zod
Di Node.js, environment variable masuk sebagai string. PORT=3000 pun tetap string, jadi z.coerce.number() diperlukan untuk konversi dan validasi range.
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 ?? "",
};
}
Tes lokal:
cp .env.example .env.local
npx tsx src/config/env.ts
Lalu minta Claude Code mencari akses process.env yang tersebar:
Cari semua pembacaan process.env langsung di repository ini.
Hanya src/config/env.ts yang boleh membaca process.env langsung.
Untuk file lain, usulkan perubahan agar mengimpor env dari src/config/env.ts.
Jangan mencetak secrets di logs, errors, atau test snapshots.
3. Redact sebelum logging
Kebocoran secret tidak hanya terjadi di Git. CI logs, output debug, error monitoring, rekaman layar, dan terminal output yang ditempel ke Claude Code juga bisa menjadi jalur bocor.
// 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 adalah pagar terakhir. Desain log yang lebih baik adalah tidak pernah menerima nilai secret sejak awal.
4. Inject dari CI/CD Secrets
GitHub Actions bisa memberikan repository, environment, atau organization secrets ke workflow. Jangan gunakan credential production untuk test PR biasa; buat nilai khusus CI dengan scope kecil.
# .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
Jangan berasumsi semua workflow menerima secrets. Fork PR, reusable workflow, dan event otomatis bisa memiliki batasan. Buat job validasi eksplisit dan jangan menulis secret ke file yang dihasilkan.
5. Catatan Docker, Vercel, Cloudflare
Di Docker, jangan menulis ENV API_KEY=... di Dockerfile. Untuk local testing, env file bisa dipakai; untuk production, gunakan secret store atau orchestrator runtime.
# local only
docker run --rm --env-file .env.local my-app:latest
Jika runtime memberikan secret sebagai file, dukung konvensi NAME_FILE:
// 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();
}
Di Vercel, pisahkan Production, Preview, dan Development. Perhatikan prefix yang terekspos ke browser seperti NEXT_PUBLIC_. Di Cloudflare Workers, nilai bisa datang lewat bindings, parameter env, atau platform Secrets. Jangan mengklaim satu contoh cocok untuk semua runtime; jadikan schema sebagai sumber kebenaran dan dokumentasikan titik injection per platform.
Review konfigurasi environment untuk Vercel, Cloudflare, dan Docker.
Jangan membaca atau meminta nilai production asli.
Cek required key names, batas public vs secret, build-time vs runtime, dan catatan rotasi.
6. Tulis playbook rotasi lebih dulu
Rotasi Secrets jangan dibuat mendadak saat incident:
- Identifikasi scope: service, environment, permission, owner
- Buat nilai baru dengan permission minimum
- Tambahkan sebagai
*_NEXTjika dual-running memungkinkan - Deploy support untuk nilai lama dan baru dalam window singkat
- Alihkan traffic dan cek health check
- Revoke nilai lama
- Cari exposure di Git history, CI logs, monitoring logs, dan prompts
- Update
.env.exampledan catatan operasional
Webhook secret, API key, dan password database punya mekanisme rotasi berbeda. Tulis owner, window, dan rollback untuk masing-masing.
Kegagalan umum
| Kegagalan | Penyebab | Perbaikan |
|---|---|---|
.env ter-commit | .gitignore terlambat ditambahkan | Revoke key segera; membersihkan history saja tidak cukup |
Secret masuk NEXT_PUBLIC_ | Prefix publik tidak dipahami | Pisahkan naming rule public/private |
console.log(process.env) | Debug terburu-buru | Pakai redaction dan review log |
| Production gagal start | Required key belum diset | Jalankan src/config/env.ts di CI |
| Nilai lokal masuk build production | Build-time/runtime tercampur | Dokumentasikan injection per platform |
| Key asli ditempel ke Claude Code | Prompt disamakan dengan secret sharing | Bagikan nama key dan rules saja |
Prompt Claude Code siap pakai
Implementasikan environment variable management untuk project ini.
Requirements:
- Buat .env.example dan .env.production.example
- Pastikan .env, .env.*, dan .dev.vars* tidak masuk Git
- Tambahkan Zod schema di src/config/env.ts dan gagal startup jika nilai hilang/invalid
- Centralize direct process.env reads di src/config/env.ts
- Redact secrets di diagnostic logs
- Tambahkan GitHub Actions job yang menjalankan env validation pada pull requests
- Tulis catatan deploy singkat untuk Vercel, Cloudflare, dan Docker
Jangan membaca API key asli atau production database URL. Kerjakan hanya dari nama key dan validation rules.
Kesimpulan
Menggunakan Claude Code untuk environment management bukan berarti menempel secrets ke chat. Cara yang benar adalah membuat kontrak yang bisa divalidasi: .env.example mendeklarasikan key, Zod memvalidasi saat startup, logs di-redact, CI/CD dan platform menginjeksi nilai asli, dan tim punya playbook rotasi.
ClaudeCodeLab menyediakan konsultasi Claude Code, training tim, review keamanan repository, serta template untuk authentication, payments, CI/CD, dan content operations. Jika tim Anda ingin bergerak lebih cepat dengan Claude Code tanpa membocorkan production keys, environment management adalah salah satu fondasi pertama yang perlu distandarkan.
Di repository uji Masa, pola ini menemukan tiga masalah sebelum deploy: production key yang belum ada, Webhook secret yang berpotensi masuk log, dan .env.example yang sudah usang. Validasi startup dengan Zod sederhana, tetapi mengubah konfigurasi dari pengetahuan pribadi menjadi kontrak tim yang bisa dipaksa.
PDF gratis: cheatsheet Claude Code
Masukkan email dan unduh satu halaman berisi command, kebiasaan review, dan workflow aman.
Kami menjaga datamu dan tidak mengirim spam.
Tentang penulis
Masa
Engineer yang berfokus pada workflow Claude Code praktis dan adopsi tim.
Artikel terkait
Permission safety ladder Claude Code: perluas akses tanpa kehilangan kontrol
Naik dari read-only ke edit terbatas, command bukti, dan cek deploy dengan kontrol yang jelas.
Claude Code Small PR Proof Pack: perubahan kecil yang mudah direview
Paket bukti untuk PR Claude Code: diff, check, URL publik, jalur CTA, dan rollback.
Review gate Claude Code sebelum commit: diff, test, URL publik, dan CTA
Cara memakai Claude Code sebelum commit: diff scope, build, URL publik, link Gumroad, CTA konsultasi, missing test, dan file tidak terkait.