Comprehensive rules for secure, maintainable use of environment variables in backend services.
You know the drill. It's 2 AM, production is down, and the culprit? A missing environment variable that worked fine in staging. Or worse—a hardcoded API key that just got rotated by security. These Cursor Rules eliminate the guesswork and late-night scrambles that plague backend deployments.
Backend services fail in predictable ways:
.env files, CI configs, and deployment scriptsThese aren't edge cases—they're the daily reality of managing configuration across development, staging, and production environments.
These Cursor Rules establish a bulletproof environment variable system that:
Validates everything at startup using schema validation with immediate failure and clear error messages. No more discovering missing variables when that specific code path executes.
Centralizes all configuration in a single, strongly-typed config module that eliminates scattered process.env calls throughout your codebase.
Enforces security by default with automatic secret masking, rotation reminders, and patterns that prevent accidental commits.
Handles the complexity of different environments, container deployments, and CI/CD pipelines with proven patterns.
Eliminate Configuration Debugging
// Before: Scattered, unvalidated access
const dbHost = process.env.DATABASE_HOST || 'localhost'; // Dangerous fallback
const apiKey = process.env.API_KEY; // Might be undefined
// After: Validated, centralized config
const config = {
dbHost: z.string().min(1).parse(process.env.APP_DB_HOST),
apiKey: z.string().min(32).parse(process.env.APP_API_KEY)
}; // Fails fast with clear error if invalid
Catch Deployment Issues Before They Hit Production Your applications now fail immediately during container startup if configuration is wrong, rather than failing mysteriously during runtime. This means faster feedback loops and confident deployments.
Secure Secret Management Without the Overhead The rules include patterns for AWS Secrets Manager, HashiCorp Vault, and CI/CD secret injection that actually work in practice—not just in tutorials.
Instead of hoping environment changes work across all environments, you get:
Rather than remembering security best practices:
No more juggling multiple .env files or wondering which variables are actually required:
npm install zod dotenv cross-env
mkdir config
// config/index.ts
import { z } from 'zod';
const EnvSchema = z.object({
NODE_ENV: z.enum(['development', 'test', 'production']),
APP_PORT: z.string().regex(/^\d+$/).transform(Number),
APP_DB_HOST: z.string().min(1),
APP_DB_PASSWORD: z.string().min(16),
FEATURE_ANALYTICS_ENABLED: z.string()
.optional()
.default('false')
.transform(v => v === 'true')
});
try {
const env = EnvSchema.parse(process.env);
export const config = Object.freeze(env);
} catch (error) {
console.error('❌ Invalid environment configuration:', error.errors);
process.exit(1);
}
// index.ts
import { config } from './config';
// Config is validated before any application code runs
const app = express();
app.listen(config.APP_PORT, () => {
console.log(`Server running on port ${config.APP_PORT}`);
});
# .env.example (commit this)
APP_PORT=3000
APP_DB_HOST=localhost
APP_DB_PASSWORD=your-secure-password-here
FEATURE_ANALYTICS_ENABLED=false
# .env (never commit this)
APP_PORT=3000
APP_DB_HOST=localhost
APP_DB_PASSWORD=actual-secure-password
FEATURE_ANALYTICS_ENABLED=true
Deployment Confidence: Configuration errors surface immediately during container startup, not during runtime. You'll know within seconds if a deployment will work.
Debugging Speed: No more tracing through application logs to find missing variables. Clear, actionable error messages tell you exactly what's wrong.
Security Posture: Automatic patterns prevent the most common configuration security mistakes. Your secrets stay secret without extra effort.
Development Velocity: Adding new configuration becomes a simple schema update. The type system catches integration issues before they reach code review.
Team Consistency: Every service follows the same configuration patterns. New team members understand the system immediately.
These rules turn environment variable management from a source of production incidents into a reliable, secure foundation for your backend services. Your future 2 AM self will thank you.
You are an expert in Node.js (JavaScript/TypeScript), Docker, Kubernetes, CI/CD (GitHub Actions, GitLab CI), HashiCorp Vault, AWS Parameter Store/Secrets Manager, Terraform, and Ansible.
---
Key Principles
- Treat every piece of configuration as data, never code.
- Never commit secrets (passwords, tokens, keys) to VCS—store them in a secret manager.
- Environment variables are immutable after process start; any change requires a new deployment.
- Fail fast: validate all required variables synchronously during application bootstrap.
- Use descriptive, scoped, SNAKE_CASE names (e.g. APP_DB_HOST) to avoid collisions.
- Keep the application 12-Factor compliant: the same artifact runs in any environment with different env vars.
- Avoid branching on `NODE_ENV` inside business logic; configure through variables instead.
---
JavaScript / TypeScript
- Access `process.env` only in one place: a dedicated `config/index.ts`.
- Immediately coerce and freeze values; export an immutable object.
```ts
// config/index.ts
import { z } from 'zod';
const EnvSchema = z.object({
NODE_ENV: z.enum(['development','test','production']),
APP_PORT: z.string().regex(/^\d+$/).transform(Number),
APP_DB_HOST: z.string().min(1),
APP_DB_PASSWORD: z.string().min(16),
FEATURE_X_ENABLED: z.string().optional().default('false').transform(v=>v==='true')
});
const env = EnvSchema.parse(process.env);
export const config = Object.freeze(env);
```
- Use `dotenv` (or `dotenv-expand`) **only** in local development; never in production containers.
- Always type-guard Boolean and numeric variables; raw strings lead to subtle bugs.
- Prefix build-time variables injected by bundlers (`VITE_`, `NEXT_PUBLIC_`) distinctly from runtime vars.
Directory & File Conventions
- `config/` folder holds env parsing, schemas, and helper utils.
- `.env.example` contains **only non-secret** keys with placeholders.
- Add `.env*` to `.gitignore`; commit `.env.test` only if populated with harmless test values.
---
Error Handling and Validation
- Validate inside `config/index.ts`; throw a plain `Error` with an actionable message.
```ts
try {
const cfg = EnvSchema.parse(process.env);
} catch (e) {
console.error('❌ Invalid environment configuration:', e.errors);
process.exit(1);
}
```
- Abort container start-up if any variable is missing/invalid; rely on orchestrator (Docker/K8s) to restart.
- Do **not** fallback to insecure defaults for secrets (e.g., empty passwords).
- Surface errors in a structured form so observability tools (Datadog, CloudWatch) capture them.
---
Framework-Specific Rules (Express.js example)
- Inject `config` into routers/controllers instead of calling `process.env`.
```ts
export default (cfg: Config) => (req: Request, res: Response) => {
if (!cfg.FEATURE_X_ENABLED) return res.status(404).end();
...
};
```
- Use middleware to short-circuit when critical feature flags are disabled.
- Never store env vars in request-scoped objects; they are global constants.
Container Entry-point Pattern
```sh
#!/usr/bin/env bash
set -euo pipefail
required=(APP_DB_HOST APP_DB_PASSWORD APP_PORT)
for var in "${required[@]}"; do
if [[ -z "${!var:-}" ]]; then
echo "Missing $var" >&2
exit 1
fi
done
exec node dist/index.js
```
---
Additional Sections
Testing
- Inject test-specific vars via `cross-env` or Docker Compose overrides.
- Snapshot config object in unit tests to detect unintended variable additions.
- Use `.env.test` loaded by Jest’s `setupFiles`.
Security
- Rotate secrets every 90 days automatically through AWS Secrets Manager/Vault.
- Restrict read access in Vault to service accounts only (least privilege).
- Mask sensitive vars in CI logs (`::add-mask::` in GitHub Actions).
Performance & Scalability
- Centralize retrieval (Vault Agent sidecar, AWS SDK caching) to avoid per-request network latency.
- Cache non-secret config in memory; no dynamic reload needed.
Deployment & CI/CD
- Pass secrets via CI secret storage, NOT pipeline YAML.
- Use Terraform variables to reference secret manager ARNs rather than raw values.
- Trigger redeployments on secret rotation events (e.g., AWS Secrets Manager rotation Lambda).
Observability
- Tag metrics with `NODE_ENV` and `APP_VERSION` coming from env vars to correlate releases.
---
Summary
Follow these rules to ensure environment variables in your backend remain secure, validated, and maintainable across local, CI, staging, and production environments.