ConfigMaps y Secrets
Inyecta configuración y secretos en los pods de Node.js como variables de entorno o archivos sin integrar los valores en las imágenes de Docker.
Receta
Tarjeta de receta de referencia rápida: lista para copiar y pegar.
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-secretsCuándo usarlo: Cada despliegue de Kubernetes. Configuración no secreta en ConfigMap; credenciales en Secret (o un operador de secretos externo).
Ejemplo de trabajo
# k8s/config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: api-config
data:
NODE_ENV: production
PORT: "3000"
LOG_LEVEL: info
FEATURE_EXPORT_CSV: "false"
---
apiVersion: v1
kind: Secret
metadata:
name: api-secrets
type: Opaque
stringData:
DATABASE_URL: postgres://app:CHANGE_ME@postgres:5432/app
JWT_SECRET: CHANGE_ME
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
spec:
template:
spec:
containers:
- name: api
image: ghcr.io/acme/api:sha-abc123
envFrom:
- configMapRef:
name: api-config
- secretRef:
name: api-secrets
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name// 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(["debug", "info", "warn", "error"]).default("info"),
DATABASE_URL: z.string().url(),
JWT_SECRET: z.string().min(32),
FEATURE_EXPORT_CSV: z.coerce.boolean().default(false),
POD_NAME: z.string().optional(),
});
export const env = schema.parse(process.env);Lo que esto demuestra:
- Opciones no sensibles en ConfigMap
- Secretos como variables de entorno (simples) analizadas por Zod al inicio
- API descendente
POD_NAMEpara logs estructurados
Análisis profundo
Decisión entre Config y Secret
| Datos | Almacenar en | Ejemplo |
|---|---|---|
| Nivel de log, feature flags | ConfigMap | LOG_LEVEL=info |
| URL de DB, claves de API | Secret | DATABASE_URL |
| Certificados TLS | Secret (tipo tls) o cert-manager | TLS de Ingress |
| ID de cliente OAuth público | ConfigMap | ID no sensible |
Nunca pongas secretos en ConfigMap (base64 no es cifrado).
Patrón de montaje de archivos
volumeMounts:
- name: config
mountPath: /app/config
readOnly: true
volumes:
- name: config
projected:
sources:
- configMap:
name: api-config
- secret:
name: api-secretsimport { readFileSync } from "node:fs";
const jwtSecret = readFileSync("/app/config/JWT_SECRET", "utf8").trim();Los archivos evitan los límites de tamaño de las variables de entorno y reducen el registro accidental de env. La aplicación debe leer los archivos al inicio.
Operador de Secretos Externos
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: api-secrets
spec:
refreshInterval: 1h
secretStoreRef:
name: aws-ssm
kind: ClusterSecretStore
target:
name: api-secrets
data:
- secretKey: DATABASE_URL
remoteRef:
key: /prod/api/DATABASE_URLSincroniza AWS SSM en un Secret de K8s automáticamente. Consulta Administradores de Secretos.
Equivalente a ECS
"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.
Errores comunes
- Secreto en la
ENVde la imagen - fuga del historial de capas. Solución: solo inyección del orquestador. - Registrar
process.enval inicio - fuga de credenciales. Solución: registrar solo las claves, nunca los valores. - Cambio de ConfigMap sin despliegue - los pods mantienen el entorno obsoleto hasta el reinicio. Solución: sidecar Reloader o activar el despliegue al cambiar la configuración.
- ConfigMap gigante como variable de entorno - límite de 1 MiB por fuente de entorno. Solución: montajes de archivos o dividir la configuración.
- Mismo Secret para desarrollo y producción - radio de explosión. Solución: secretos con ámbito de namespace por entorno.
- JWT en ConfigMap - tipo de almacenamiento incorrecto. Solución: Secret con RBAC limitado a la cuenta de servicio.
Alternativas
| Alternativa | Usar cuándo | No usar cuándo |
|---|---|---|
| envFrom ConfigMap + Secret | Aplicaciones Node simples de 12 factores | Árboles de configuración enormes |
| Volúmenes proyectados | Mezclar archivos + env | Servicios pequeños de un solo entorno |
| Operador de Secretos Externos | SSM/Vault como fuente de verdad | No hay administrador de clúster para CRDs |
| Doppler / Vault Agent | Plataforma de secretos centralizada | Solo tiendas nativas de K8s |
Preguntas frecuentes
¿Están cifrados los Secrets de Kubernetes?
El cifrado en reposo depende de la configuración del clúster (envoltorio KMS). RBAC sigue siendo necesario; trátalo como sensible.
¿Cómo rotamos DATABASE_URL?
Actualiza SSM/ExternalSecret, reinicia el despliegue. Usa pools de conexión que se recuperen de errores de autenticación.
¿Feature flags en ConfigMap?
Sí para flags de entorno booleanos. Los sistemas de flags complejos usan LaunchDarkly o SDK similar, no ConfigMaps gigantes.
¿`.env` en desarrollo local?
dotenv solo para el portátil. La producción usa nombres de paridad de ConfigMap/Secret - Conceptos básicos de configuración.
¿Puede NestJS ConfigModule leer archivos?
Sí. ConfigModule.forRoot({ envFilePath: undefined }) se basa en el entorno inyectado en producción.
¿Quién puede leer Secrets?
RBAC: solo la ServiceAccount api en el namespace production a través de RoleBinding.
Relacionado
- Administradores de Secretos - Obtención de SSM y Vault
- Conceptos básicos de configuración - Módulo de entorno Zod
- Despliegue de Kubernetes - montaje en Despliegue
- ECS Fargate y Cloud Run - Inyección de secretos de AWS/GCP
- Mejores prácticas de despliegue de plataforma - lista de verificación de la sección
Versiones de la pila: Esta página fue escrita para Node.js 24.18.0 (LTS activa), npm 10+, TypeScript 5.6+, Express 5, Fastify 5 y NestJS 11.