Busca en todas las páginas de la documentación
Inject configuration and secrets into Node.js pods as environment variables or files without baking values into Docker images.
Quick-reference recipe card - copy-paste ready.
apiVersion: v1
kind: ConfigMap
metadata:
name: api-config
data:
LOG_LEVEL: info
API_BASE_URL: https://api.acme.dev
---
apiVersion: v1
kind: Secret
metadata:
name: api-secrets
type: Opaque
stringData:
DATABASE_URL: postgres://user:pass@db:5432/appcontainers:
- name: api
envFrom:
- configMapRef:
name: api-config
- secretRef:
name: api-secretsWhen to reach for this: Every Kubernetes Deployment. Non-secret config in ConfigMap; credentials in Secret (or external secret operator).
# k8s/config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: api-config
data:
NODE_ENV: production
PORT: "3000"
LOG_LEVEL: info
FEATURE_EXPORT_CSV: "false"
---
// src/env.ts
import { z } from "zod";
const schema = z.object({
NODE_ENV: z.enum(["development", "production", "test"]),
PORT: z.coerce.number().default(3000),
LOG_LEVEL: z.enum
What this demonstrates:
POD_NAME for structured logs| Data | Store in | Example |
|---|---|---|
| Log level, feature flags | ConfigMap | LOG_LEVEL=info |
| DB URL, API keys | Secret | DATABASE_URL |
| TLS certs | Secret (tls type) or cert-manager | Ingress TLS |
| Public OAuth client id | ConfigMap | non-sensitive id |
Never put secrets in ConfigMap (base64 is not encryption).
volumeMounts:
- name: config
mountPath: /app/config
readOnly: true
volumes:
- name: config
projected:
sources:
- configMap:
name: api-config
import { readFileSync } from "node:fs";
const jwtSecret = readFileSync("/app/config/JWT_SECRET", "utf8").trim();Files avoid env var size limits and reduce accidental env logging. App must read files at boot.
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: api-secrets
spec:
refreshInterval: 1h
secretStoreRef:
name: aws-ssm
kind: ClusterSecretStore
target:
name
Syncs AWS SSM into K8s Secret automatically. See Secrets Managers.
"secrets": [
{
"name": "DATABASE_URL",
"valueFrom": "arn:aws:ssm:us-east-1:123:parameter/prod/api/DATABASE_URL"
}
]Cloud Run: --set-secrets DATABASE_URL=api-database-url:latest.
ENV - layer history leak. Fix: orchestrator injection only.process.env on boot - credential leak. Fix: log keys only, never values.| Alternative | Use When | Don't Use When |
|---|---|---|
| envFrom ConfigMap + Secret | Simple 12-factor Node apps | Huge config trees |
| Projected volumes | Mix files + env | Tiny single-env services |
| External Secrets Operator | SSM/Vault source of truth | No cluster admin for CRDs |
| Doppler / Vault Agent | Centralized secret platform | K8s-native only shops |
At rest encryption depends on cluster config (KMS envelope). RBAC still required; treat as sensitive.
Update SSM/ExternalSecret, rolling restart Deployment. Use connection pools that recover on auth error.
Yes for boolean env flags. Complex flag systems use LaunchDarkly or similar SDK, not giant ConfigMaps.
.env in local dev?dotenv for laptop only. Production uses ConfigMap/Secret parity names - Configuration Basics.
Yes. ConfigModule.forRoot({ envFilePath: undefined }) relies on injected env in prod.
RBAC: only api ServiceAccount in production namespace via RoleBinding.
Stack versions: This page was written for Node.js 24.18.0 (Active LTS), npm 10+, TypeScript 5.6+, Express 5, Fastify 5, and NestJS 11.