Montar Configs y Secrets en Pods de Kubernetes
Cómo montar ConfigMaps y Secrets en pods de Kubernetes usando env vars, volumes, projected volumes y secret management con external secrets.
Nota para desarrolladores hispanohablantes: Esta guía incluye ejemplos y convenciones de nomenclatura adaptadas a equipos que trabajan en español. Cuando existen diferencias significativas en terminología técnica entre el inglés y el español, se indican explícitamente para facilitar la comunicación en equipos multiculturales.
Overview
Los ConfigMaps de Kubernetes almacenan data de configuración no sensible. Los Secrets almacenan data sensible como passwords, tokens y certificados. Ambos pueden ser montados en pods como environment variables o archivos. Esta recipe cubre los patterns para inyectar configuración y secrets en tus aplicaciones que corren en Kubernetes — desde env vars básicas hasta projected volumes y external secret operators.
When to Use
- Inyectar configuración en aplicaciones containerizadas
- Manejar database connection strings, API keys y certificados
- Cuando diferentes entornos (dev, staging, prod) necesitan diferentes configs
- Cuando necesitás actualizar config sin rebuildar la imagen
- Cuando los secrets deben ser montados como archivos (e.g., TLS certificates)
When NOT to Use
- Para config no sensible que raramente cambia — bakeala en la imagen
- Cuando necesitás config templating complejo — usá un init container o Helm
- Para secrets en git — usá SealedSecrets o External Secrets Operator en su lugar
Solution
Crear un ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
namespace: default
data:
LOG_LEVEL: "info"
DATABASE_HOST: "db.internal"
APP_PORT: "8080"
config.yaml: |
server:
port: 8080
timeout: 30s
database:
pool_size: 10
timeout: 5s
Crear un Secret
apiVersion: v1
kind: Secret
metadata:
name: app-secrets
namespace: default
type: Opaque
stringData:
DATABASE_PASSWORD: "supersecret"
API_KEY: "abc123-def456"
JWT_SECRET: "my-jwt-secret-key"
Montar ConfigMap como environment variables
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 2
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: app
image: my-app:latest
envFrom:
- configMapRef:
name: app-config
env:
- name: DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: app-secrets
key: DATABASE_PASSWORD
- name: API_KEY
valueFrom:
secretKeyRef:
name: app-secrets
key: API_KEY
Montar ConfigMap como volume
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 2
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: app
image: my-app:latest
volumeMounts:
- name: config-volume
mountPath: /etc/app/config
readOnly: true
volumes:
- name: config-volume
configMap:
name: app-config
items:
- key: config.yaml
path: config.yaml
Montar Secret como volume
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 2
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: app
image: my-app:latest
volumeMounts:
- name: secrets-volume
mountPath: /etc/app/secrets
readOnly: true
volumes:
- name: secrets-volume
secret:
secretName: app-secrets
defaultMode: 0400 # Read-only for owner
Montar TLS certificate como volume
apiVersion: v1
kind: Secret
metadata:
name: tls-secret
type: kubernetes.io/tls
data:
tls.crt: <base64-encoded-cert>
tls.key: <base64-encoded-key>
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 2
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: app
image: my-app:latest
volumeMounts:
- name: tls
mountPath: /etc/tls
readOnly: true
volumes:
- name: tls
secret:
secretName: tls-secret
Projected volume (combinar ConfigMap + Secret)
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 2
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: app
image: my-app:latest
volumeMounts:
- name: combined-config
mountPath: /etc/app
readOnly: true
volumes:
- name: combined-config
projected:
sources:
- configMap:
name: app-config
- secret:
name: app-secrets
Env vars selectivos desde ConfigMap
spec:
containers:
- name: app
image: my-app:latest
env:
- name: LOG_LEVEL
valueFrom:
configMapKeyRef:
name: app-config
key: LOG_LEVEL
- name: APP_PORT
valueFrom:
configMapKeyRef:
name: app-config
key: APP_PORT
- name: DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: app-secrets
key: DATABASE_PASSWORD
Subpath para montar un solo archivo
spec:
containers:
- name: app
image: my-app:latest
volumeMounts:
- name: config-volume
mountPath: /app/config.yaml
subPath: config.yaml
readOnly: true
volumes:
- name: config-volume
configMap:
name: app-config
ConfigMaps y Secrets immutables
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
immutable: true
data:
LOG_LEVEL: "info"
APP_PORT: "8080"
External Secrets Operator
# ExternalSecret — sincroniza secrets desde AWS Secrets Manager / Vault
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: app-secrets
spec:
refreshInterval: 1h
secretStoreRef:
name: aws-secrets-manager
kind: ClusterSecretStore
target:
name: app-secrets
creationPolicy: Owner
data:
- secretKey: DATABASE_PASSWORD
remoteRef:
key: my-app/database
property: password
- secretKey: API_KEY
remoteRef:
key: my-app/api
property: key
Sealed Secrets para GitOps
# SealedSecret — secret encrypted seguro para guardar en git
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
name: app-secrets
spec:
encryptedData:
DATABASE_PASSWORD: AgB...
API_KEY: AgC...
template:
metadata:
name: app-secrets
type: Opaque
Init container con config rendering
spec:
initContainers:
- name: config-renderer
image: my-config-renderer:latest
env:
- name: APP_ENV
value: "production"
volumeMounts:
- name: config-template
mountPath: /templates
- name: rendered-config
mountPath: /output
command:
- sh
- -c
- |
envsubst < /templates/config.yaml.tpl > /output/config.yaml
containers:
- name: app
image: my-app:latest
volumeMounts:
- name: rendered-config
mountPath: /etc/app/config
readOnly: true
volumes:
- name: config-template
configMap:
name: app-config-template
- name: rendered-config
emptyDir: {}
Variants
Usar envFrom con prefix
spec:
containers:
- name: app
image: my-app:latest
envFrom:
- configMapRef:
name: app-config
prefix: APP_
- secretRef:
name: app-secrets
prefix: SECRET_
Hot-reloading de ConfigMap en un volume
# Los ConfigMaps montados como volumes se auto-updatean (típicamente dentro de 60s)
# Usá un file watcher en tu app para pick up changes
spec:
containers:
- name: app
image: my-app:latest
volumeMounts:
- name: config
mountPath: /etc/app/config
readOnly: true
# La app debería watchear file changes
# e.g., fsnotify in Go, chokidar in Node.js
volumes:
- name: config
configMap:
name: app-config
Docker config secret para image pulls
apiVersion: v1
kind: Secret
metadata:
name: registry-credentials
type: kubernetes.io/dockerconfigjson
data:
.dockerconfigjson: <base64-encoded-docker-config>
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
template:
spec:
imagePullSecrets:
- name: registry-credentials
containers:
- name: app
image: private-registry.io/my-app:latest
Best Practices
-
For a deeper guide, see Kubernetes Config Management Guide.
-
Usá ConfigMaps para data no sensible, Secrets para data sensible — nunca al revés
-
Seteá
readOnly: trueen volume mounts — previene que las apps modifiquen config en runtime -
Usá
immutable: truepara configs estables — mejora performance y previene cambios accidentales -
Usá
defaultMode: 0400para secret volumes — restringe file permissions -
Usá External Secrets Operator para producción — mantiene secrets fuera de etcd y git
-
Usá SealedSecrets para GitOps — encrypta secrets at rest en git
-
No guardes secrets en ConfigMaps — no están encrypted at rest en etcd
-
Usá
envFrompara bulk loading — más limpio que entries individuales deenv
Common Mistakes
- Guardar secrets en ConfigMaps: los ConfigMaps no están encrypted. Usá Secrets (que están base64-encoded, igual no están truly encrypted — usá ESO o SealedSecrets para encryption real).
- No setear
readOnly: true: las apps pueden modificar config files montados, causando comportamiento inconsistente a través de pod restarts. - Montar todo el ConfigMap cuando necesitás un archivo: usá
subPathpara montar un solo archivo sin sobrescribir el directorio. - Olvidar que los Secrets son base64, no encrypted: cualquiera con
kubectl get secretpuede decodearlos. Usá RBAC para restringir acceso. - No usar
immutable: true: los ConfigMaps mutables causan pod restarts en update. Los immutables son más seguros y rápidos.
FAQ
¿Cuál es la diferencia entre ConfigMap y Secret?
Los ConfigMaps almacenan data no sensible como plaintext. Los Secrets almacenan data sensible como strings base64-encoded. Los Secrets tienen controles RBAC adicionales y pueden ser encrypted at rest en etcd.
¿Los Kubernetes Secrets están encrypted?
Por default, los Secrets están base64-encoded (no encrypted). Habilitá encryption at rest en etcd con --encryption-provider-config. Para encryption real, usá External Secrets Operator con AWS KMS o HashiCorp Vault.
¿Cómo actualizo un ConfigMap sin restartear pods?
Los ConfigMaps montados como volumes se auto-updatean (dentro de ~60s). Los ConfigMaps cargados como env vars requieren un pod restart. Usá un file watcher en tu app para hot-reloading de configs montados como volume.
¿Qué es un projected volume?
Un volume que combina múltiples sources (ConfigMaps, Secrets, downward API) en un solo directorio. Útil para montar config y secrets juntos sin múltiples volume mounts.
¿Cómo creo un Secret desde un archivo?
kubectl create secret generic app-secrets \
--from-file=ssl.crt=certs/tls.crt \
--from-file=ssl.key=certs/tls.key \
--from-literal=api-key=abc123 Recursos Relacionados
Package Kubernetes Manifests with Helm Charts
How to create, template, and deploy Kubernetes applications using Helm charts, covering values, conditionals, ranges, hooks, subcharts, and library charts.
RecipeOverride Docker Compose Configs per Environment
How to use Docker Compose override files for environment-specific configurations, covering dev, test, staging, production, profiles, and secrets management.
RecipeStore Terraform State in S3 with DynamoDB Locking
How to configure Terraform remote state with S3 backend and DynamoDB locking, covering state isolation, workspace management, encryption, and CI/CD integration.
RecipeSetup SSL Certificates with Let's Encrypt
How to obtain, install, and auto-renew SSL certificates using Certbot with Nginx, Apache, and standalone modes for HTTPS-enabled deployments.