Claude Code Dev Environment Setup: Safe, Reproducible Workflow
Set up Claude Code with Node, Docker, .env rules, permissions, hooks, and repeatable checks.
A new laptop or a freshly cloned repository should not cost half a day before the first useful commit. The usual failures are predictable: the wrong Node version, mixed package managers, a stale .env, Docker volumes from an old schema, and undocumented local commands. Claude Code can remove much of that friction, but only if the environment is set up with boundaries first.
The goal is not “let the agent do everything.” The goal is a reproducible setup where every developer and every Claude Code session sees the same instructions, the same package manager, the same secret-handling rules, and the same verification commands.
Use official documentation as the source of truth: Claude Code setup, settings, permissions, and hooks. For related ClaudeCodeLab reading, keep Claude Code getting started, CLAUDE.md best practices, and hooks guide nearby.
flowchart TD
A["Check local tools"] --> B["Pin Node and package manager"]
B --> C["Create dependencies and .env.example"]
C --> D["Write CLAUDE.md setup instructions"]
D --> E["Limit permissions in settings.json"]
E --> F["Block dangerous commands with hooks"]
F --> G["Run doctor, env check, and tests"]
The Setup Policy
Treat the dev environment like production code. If the rule matters, commit it. If a secret matters, keep it out of the agent context. If a command can delete data or publish code, make it ask a human first.
| Area | File or command | Why it matters |
|---|---|---|
| Runtime | .nvmrc, packageManager, Corepack | Prevents Node and package-manager drift |
| Secrets | .env.example, .gitignore, permissions.deny | Keeps real credentials out of prompts and commits |
| Project memory | CLAUDE.md | Gives Claude Code the same setup rules every session |
| Permissions | .claude/settings.json | Controls file reads, Bash commands, and default mode |
| Hooks | .claude/hooks/* | Enforces deterministic checks before tools run |
| Verification | doctor, check:env, test | Replaces “it seems fine” with command output |
Copy-Paste Bootstrap
This script targets Git Bash, WSL, macOS, or Linux. It creates a minimal TypeScript project that validates environment variables, pins pnpm, denies secret reads, and installs a PreToolUse hook for risky shell commands.
#!/usr/bin/env bash
set -euo pipefail
APP_DIR="${1:-claude-dev-lab}"
mkdir -p "$APP_DIR"
cd "$APP_DIR"
command -v node >/dev/null || { echo "Node.js is required"; exit 1; }
command -v claude >/dev/null || { echo "Claude Code CLI is required"; exit 1; }
corepack enable
corepack prepare pnpm@9.15.4 --activate
cat > package.json <<'JSON'
{
"name": "claude-dev-lab",
"private": true,
"type": "module",
"packageManager": "pnpm@9.15.4",
"scripts": {
"doctor": "node --version && pnpm --version && claude --version",
"check:env": "tsx src/env.ts",
"test": "vitest run --passWithNoTests"
},
"dependencies": {
"dotenv": "latest",
"zod": "latest"
},
"devDependencies": {
"@types/node": "latest",
"tsx": "latest",
"typescript": "latest",
"vitest": "latest"
}
}
JSON
mkdir -p src .claude/hooks .vscode
printf "22\n" > .nvmrc
cat > .gitignore <<'EOF'
node_modules
.env
.env.*
!.env.example
dist
coverage
EOF
cat > .env.example <<'EOF'
NODE_ENV=development
DATABASE_URL=postgresql://app:app@localhost:5432/app
REDIS_URL=redis://localhost:6379
EOF
cat > src/env.ts <<'TS'
import { config } from "dotenv";
import { z } from "zod";
config();
const Env = z.object({
NODE_ENV: z.enum(["development", "test", "production"]).default("development"),
DATABASE_URL: z.string().url(),
REDIS_URL: z.string().url().optional()
});
const parsed = Env.safeParse(process.env);
if (!parsed.success) {
console.error(parsed.error.flatten().fieldErrors);
process.exit(1);
}
console.log("env ok", {
nodeEnv: parsed.data.NODE_ENV,
hasRedis: Boolean(parsed.data.REDIS_URL)
});
TS
cat > CLAUDE.md <<'EOF'
# Project Instructions
## Environment Setup
- Use Node from `.nvmrc`.
- Use pnpm through Corepack. Do not switch to npm or yarn.
- Copy `.env.example` to `.env` locally, then edit values by hand.
- Never read, print, or commit `.env` or secret files.
- Before changing code, run `pnpm run doctor` and `pnpm run check:env`.
- After changing code, run the narrowest relevant test and record the command result.
## Work Rules
- Start with exploration and a short plan.
- Do not run destructive commands or deploy commands without explicit human approval.
- Keep setup changes reproducible in committed files, not in local terminal history.
EOF
cat > .claude/hooks/block-dangerous.mjs <<'JS'
import { readFileSync } from "node:fs";
const input = JSON.parse(readFileSync(0, "utf8") || "{}");
const command = String(input.tool_input?.command ?? "");
const blockedPatterns = [
/rm\s+-rf\s+(\/|~|\$HOME)/,
/git\s+push\b/,
/curl\b.+\|\s*(bash|sh)/,
/Invoke-WebRequest\b.+\|\s*iex/i
];
if (blockedPatterns.some((pattern) => pattern.test(command))) {
console.log(JSON.stringify({
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: "Blocked by project hook. Ask a human before destructive or remote-executed commands."
}
}));
} else {
console.log("{}");
}
JS
cat > .claude/settings.json <<'JSON'
{
"defaultMode": "plan",
"permissions": {
"allow": [
"Read",
"Bash(pnpm install)",
"Bash(pnpm run *)",
"Bash(git status *)",
"Bash(claude --version)",
"Bash(claude doctor)"
],
"deny": [
"Read(./.env)",
"Read(./.env.*)",
"Read(./secrets/**)",
"Bash(git push *)",
"Bash(rm -rf *)"
]
},
"env": {
"CLAUDE_CODE_SUBPROCESS_ENV_SCRUB": "1"
},
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "node .claude/hooks/block-dangerous.mjs"
}
]
}
]
}
}
JSON
pnpm install
cp .env.example .env
pnpm run doctor
pnpm run check:env
pnpm test
On native Windows, check the toolchain before asking Claude Code to touch the repository:
winget install Anthropic.ClaudeCode
claude --version
claude doctor
node --version
corepack enable
pnpm --version
If pnpm --version fails, fix Node/Corepack first. If claude doctor reports proxy, certificate, or authentication issues, copy the exact error into the task and ask Claude Code to diagnose within that constraint instead of guessing.
A Safer Prompt
Do not ask for “set up everything” with no boundary. Give Claude Code the allowed files, forbidden files, and receipt you expect.
claude -p "
Audit and complete the dev environment setup for this repository.
Rules:
- Do not read .env, .env.*, or files under secrets/
- Follow packageManager; do not switch from pnpm to npm or yarn
- Do not delete Docker volumes or run git push
Allowed:
- Read README, package.json, CLAUDE.md, and .claude/settings.json
- Run pnpm install, pnpm run doctor, pnpm run check:env, and pnpm test
Return a short receipt with commands run, files changed, failures found, and remaining manual steps.
"
Practical Use Cases
| Use case | How this setup helps |
|---|---|
| New SaaS prototype | Add Docker Compose for PostgreSQL and Redis, then keep app setup reproducible from day one |
| Existing team repository | Let Claude Code read docs and config, run approved checks, and update missing onboarding steps |
| Content or product site | Protect CTA links, tracking events, OGP images, and AdSense-sensitive pages while editing code |
| Internal business tool | Standardize DB seed, queues, mocks, and test commands so a new developer can run it in 30 minutes |
For Docker-heavy projects, continue with Docker Compose setup. For team workflows, read team collaboration. For CI, connect the same verification commands to CI/CD setup.
Failure Modes to Catch Early
Mixed package managers are the most common source of drift. If the repository has pnpm-lock.yaml, do not let an agent run npm install. Put the rule in CLAUDE.md, keep packageManager in package.json, and deny ad hoc install commands if the team needs stricter control.
The second failure is secret exposure. .env.example is reviewable; .env is not. Use .gitignore and permissions.deny together. Claude Code can still help by scanning source code for required environment variable names and updating .env.example with dummy values.
Docker volumes are another trap. A stale database volume can make a migration look broken when the schema is simply old. Ask Claude Code to explain the target volume and risk before suggesting any removal command.
Avoid bypassPermissions on your host machine. It is useful only inside an isolated VM or container where file damage is contained. Start in plan or default, then approve only the commands needed for the task.
Finally, inspect hooks in untrusted repositories before starting a session. Hooks run automatically, so a copied .claude/settings.json deserves the same review as a shell script.
Reproducibility Checklist
claude --versionandclaude doctorpass.nvmrcor.node-versionexistspackage.jsoncontainspackageManager- Only one lockfile is present
.env.exampleis current and.envis ignoredCLAUDE.mddocuments setup, forbidden actions, and verification commands.claude/settings.jsondenies secret reads,git push, and destructive commands- Hooks are short, readable, and committed with the project
- The final receipt includes commands run and test results
Monetization Note
Environment setup affects revenue work too. A broken local setup can silently break pricing pages, analytics events, purchase links, or newsletter forms. If you are turning Claude Code into a repeatable content or product workflow, start with the free cheat sheet, use the templates in products, or bring the workflow into a team through training.
Result From Trying This
I tested the flow by creating the minimal project, installing dependencies, copying .env.example, and running pnpm run doctor, pnpm run check:env, and pnpm test in that order. The most useful safeguards were the .env deny rules and the hook that blocks git push or destructive shell commands. In Masa’s day-to-day workflow, setup failures usually come from missing assumptions rather than missing commands, so the key improvement is keeping those assumptions in committed files.
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.
About the Author
Masa
Engineer focused on practical Claude Code workflows. Runs claudecode-lab.com, a 10-language technical media site.
Related Posts
Claude Code Obsidian to CLAUDE.md Workflow: Stop Re-explaining Context
Turn Obsidian working notes into concise CLAUDE.md operating notes that make Claude Code sessions easier to resume.
Claude Code Revenue CTA Routing: Send Articles to PDF, Gumroad, and Consultation
A Claude Code workflow for routing article readers to the free PDF, Gumroad products, or consultation by intent.
Claude Code Team Handoff Rules: Review Evidence, Permissions, Rollback, and Revenue Paths
A practical Claude Code handoff format for team review, proof, permission rules, rollback, free PDF, Gumroad, and consultation paths.
Related Products
The Complete Claude Code Setup & Configuration Guide
From install to team-ready workflow.
A practical guide to installation, CLAUDE.md, hooks, MCP servers, permissions, IDE setup, and CI/CD workflows.
50 Battle-Tested Claude Code Prompt Templates
Copy, paste, ship. 50 production-ready prompts.
Use proven prompts for code review, refactoring, testing, documentation, debugging, architecture, and incident response.