Guía de Kubernetes Config Management
Dominá Kubernetes config management: ConfigMaps, Secrets, External Secrets Operator, sealed secrets, env injection, volume mounts y patrones de config rotation.
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.
Introducción
Kubernetes separa configuration de container images usando ConfigMaps y Secrets. ConfigMaps storean non-sensitive configuration como key-value pairs. Secrets storean sensitive data como passwords y tokens. External Secrets Operator y Sealed Secrets extienden estos para GitOps workflows. A continuación: ConfigMaps, Secrets, environment injection, volume mounts, External Secrets Operator, Sealed Secrets y config rotation patterns.
ConfigMaps
Creá un ConfigMap
# k8s/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
namespace: production
data:
# Key-value pairs
LOG_LEVEL: "info"
DATABASE_HOST: "db.internal"
DATABASE_PORT: "5432"
REDIS_URL: "redis://redis:6379"
# Full file content como un key
nginx.conf: |
server {
listen 80;
location / {
proxy_pass http://backend:3000;
}
}
config.json: |
{
"feature_flags": {
"new_ui": true,
"beta_features": false
},
"limits": {
"max_connections": 100
}
}
Creá from files
# Creá ConfigMap desde un single file
kubectl create configmap nginx-config --from-file=nginx.conf
# Creá from multiple files
kubectl create configmap app-config \
--from-file=config.json \
--from-file=settings.yaml \
--from-file=nginx.conf
# Creá from a directory
kubectl create configmap app-config --from-file=./config/
# Creá from literal values
kubectl create configmap app-config \
--from-literal=LOG_LEVEL=info \
--from-literal=DATABASE_HOST=db.internal
Inyectá ConfigMap como environment variables
# k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: app
spec:
template:
spec:
containers:
- name: app
image: my-app:latest
env:
# Single value from ConfigMap
- name: LOG_LEVEL
valueFrom:
configMapKeyRef:
name: app-config
key: LOG_LEVEL
- name: DATABASE_HOST
valueFrom:
configMapKeyRef:
name: app-config
key: DATABASE_HOST
# All ConfigMap keys como env vars
envFrom:
- configMapRef:
name: app-config
optional: false # Pod fails si ConfigMap no existe
Mounteá ConfigMap como un file
apiVersion: apps/v1
kind: Deployment
metadata:
name: app
spec:
template:
spec:
containers:
- name: app
image: my-app:latest
volumeMounts:
- name: config-volume
mountPath: /etc/config
readOnly: true
volumes:
- name: config-volume
configMap:
name: app-config
# Optional: specificá cuáles keys incluir
items:
- key: config.json
path: config.json
- key: nginx.conf
path: nginx.conf
Mounteá ConfigMap con subPath (single file)
volumes:
- name: config-volume
configMap:
name: app-config
containers:
- name: app
volumeMounts:
- name: config-volume
mountPath: /app/config.json
subPath: config.json # Mountea solo este key
readOnly: true
Secrets
Creá un Secret
# k8s/secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: app-secrets
namespace: production
type: Opaque
data:
# Base64-encoded values
DATABASE_PASSWORD: cGFzc3dvcmQxMjM= # echo -n "password123" | base64
API_KEY: YWJjZGVmZ2hpamtsbW5vcA==
JWT_SECRET: c3VwZXItc2VjcmV0LWtleQ==
stringData:
# Plain text values (auto-encoded by Kubernetes)
DATABASE_URL: "postgresql://user:pass@db:5432/myapp"
STRIPE_KEY: "sk_live_abc123"
Creá from CLI
# Creá from literal values
kubectl create secret generic app-secrets \
--from-literal=DATABASE_PASSWORD=password123 \
--from-literal=API_KEY=abcdefg
# Creá from file
kubectl create secret generic tls-secret \
--from-file=tls.crt=cert.pem \
--from-file=tls.key=key.pem
# Creá Docker registry secret
kubectl create secret docker-registry registry-secret \
--docker-server=registry.io \
--docker-username=user \
--docker-password=pass \
--docker-email=email@example.com
Inyectá Secret como environment variables
containers:
- name: app
env:
- name: DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: app-secrets
key: DATABASE_PASSWORD
- name: API_KEY
valueFrom:
secretKeyRef:
name: app-secrets
key: API_KEY
# All Secret keys como env vars
envFrom:
- secretRef:
name: app-secrets
Mounteá Secret como files
volumes:
- name: secret-volume
secret:
secretName: app-secrets
defaultMode: 0400 # Read-only by owner
containers:
- name: app
volumeMounts:
- name: secret-volume
mountPath: /etc/secrets
readOnly: true
TLS Secret
apiVersion: v1
kind: Secret
metadata:
name: tls-secret
type: kubernetes.io/tls
data:
tls.crt: <base64-encoded-cert>
tls.key: <base64-encoded-key>
# Creá TLS secret from files
kubectl create secret tls tls-secret \
--cert=cert.pem \
--key=key.pem
External Secrets Operator
Installéa ESO
# Installéa via Helm
helm install external-secrets \
external-secrets/external-secrets \
--namespace external-secrets \
--create-namespace
SecretStore (connectea a Vault)
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
name: vault-backend
namespace: production
spec:
provider:
vault:
server: "https://vault.internal:8200"
path: "kv"
version: "v2"
auth:
kubernetes:
mountPath: "kubernetes"
role: "external-secrets"
serviceAccountRef:
name: external-secrets
ExternalSecret (syncea from Vault a Kubernetes Secret)
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: app-secrets
namespace: production
spec:
refreshInterval: 1h # Sync every hour
secretStoreRef:
name: vault-backend
kind: SecretStore
target:
name: app-secrets # Name del Kubernetes Secret a crear
creationPolicy: Owner
data:
- secretKey: DATABASE_PASSWORD
remoteRef:
key: production/database
property: password
- secretKey: API_KEY
remoteRef:
key: production/api
property: key
- secretKey: JWT_SECRET
remoteRef:
key: production/auth
property: jwt_secret
AWS Secrets Manager SecretStore
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
name: aws-secrets
spec:
provider:
aws:
service: SecretsManager
region: us-east-1
auth:
jwt:
serviceAccountRef:
name: external-secrets
Sealed Secrets
Installéa Sealed Secrets controller
helm install sealed-secrets \
sealed-secrets/sealed-secrets \
--namespace kube-system \
--create-namespace
Sealéa un secret
# Creá un regular secret, luego sealealo
echo -n 'password123' | base64 > secret.yaml
cat > secret.yaml <<EOF
apiVersion: v1
kind: Secret
metadata:
name: app-secrets
namespace: production
type: Opaque
data:
DATABASE_PASSWORD: cGFzc3dvcmQxMjM=
EOF
# Sealealo (safe para commitear a Git)
kubeseal --format yaml < secret.yaml > sealed-secret.yaml
SealedSecret resource
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
name: app-secrets
namespace: production
spec:
encryptedData:
DATABASE_PASSWORD: AgB...encrypted-blob...
template:
metadata:
name: app-secrets
namespace: production
type: Opaque
Config Rotation
Restarteá pods en ConfigMap change
# ConfigMap change no auto-restartea pods.
# Usá un hash annotation para force restart en config change.
apiVersion: apps/v1
kind: Deployment
metadata:
name: app
spec:
template:
metadata:
annotations:
# Hash del ConfigMap content — cambia cuando ConfigMap updates
checksum/config: "{{ .Values.configHash }}"
spec:
containers:
- name: app
envFrom:
- configMapRef:
name: app-config
Reloader (automatic restart en config change)
# Installéa Reloader — auto-restartea pods cuando ConfigMaps/Secrets cambian
helm install reloader stakater/reloader \
--namespace reloader \
--create-namespace
# Annotateá deployment para trigger restart en ConfigMap/Secret change
apiVersion: apps/v1
kind: Deployment
metadata:
name: app
annotations:
configmap.reloader.stakater.com/reload: "app-config"
secret.reloader.stakater.com/reload: "app-secrets"
Best Practices
-
For a deeper guide, see Mount Configs and Secrets into Kubernetes Pods.
-
Usá ConfigMaps para non-sensitive config — environment variables, feature flags, config files
-
Usá Secrets para sensitive data — passwords, tokens, API keys, certificates
-
Nunca storees Secrets en Git como plain text — usá External Secrets Operator o Sealed Secrets
-
Usá
envFrompara bulk injection — cleaner que listing each variable individually -
Usá volume mounts para config files — habilita hot reload sin pod restart (con Reloader)
-
Usá External Secrets Operator para production — syncea desde Vault, AWS Secrets Manager, GCP Secret Manager
-
Usá Sealed Secrets para GitOps — encrypted secrets safe para commitear a version control
-
Seteá
optional: falseen critical ConfigMaps — pod fails fast si config está missing -
Usá
defaultMode: 0400en Secret volumes — restrictí file permissions -
Usá Reloader para automatic restarts — pods pickéan up config changes sin manual intervention
-
Namespaceá tu config — usá separate ConfigMaps per application o service
-
Usá
immutable: truepara static config — improvea performance y prevente accidental changes
Common Mistakes
- Storear secrets en ConfigMaps: ConfigMaps no son encrypted. Usá Secrets o External Secrets Operator.
- Commitear base64 secrets a Git: base64 es encoding, no encryption. Anyone puede decode. Usá Sealed Secrets o ESO.
- No config rotation: updatear un ConfigMap no restartea pods. Usá Reloader o checksum annotations.
- Mountear entire ConfigMap cuando necesitás un file: usá
subPathpara mountear un single key como un file. - No namespace scoping: ConfigMaps son namespace-scoped. Un ConfigMap en
defaultnamespace es invisible aproduction. - Usar
stringDataydatapara el mismo key:datatakes precedence. Usá one or the other per key.
FAQ
¿Cuál es la diferencia entre ConfigMap y Secret?
ConfigMaps storean non-sensitive configuration como plain key-value pairs. Secrets storean sensitive data con base64 encoding y additional access controls (RBAC, etcd encryption at rest). Usá ConfigMaps para app config, Secrets para credentials.
¿Están encrypted los Kubernetes Secrets?
By default, Secrets son base64-encoded (no encrypted). Habilitá encryption at rest en etcd con --encryption-provider-config. Para true secret management, usá External Secrets Operator con Vault o cloud secret managers.
¿Qué es External Secrets Operator?
Un Kubernetes operator que syncea secrets desde external secret managers (Vault, AWS Secrets Manager, GCP Secret Manager) into Kubernetes Secrets. Definís un ExternalSecret resource que referencea un SecretStore, y ESO crea y updatea el Kubernetes Secret automáticamente.
¿Qué son Sealed Secrets?
Un controller que encryptea Kubernetes Secrets into SealedSecret resources. El encrypted SealedSecret es safe para storear en Git. Solo el controller en tu cluster puede decryptarlo. Useful para GitOps workflows donde todo vive en version control.
¿Cómo updateo pod config sin restarting?
ConfigMaps mounted como volumes son updated automáticamente (within 60-90 seconds por el kubelet). ConfigMaps injected como environment variables requiren un pod restart. Usá Reloader para automatizar restarts cuando ConfigMaps o Secrets cambian.
Recursos Relacionados
Helm Charts: Structure, Templating, Dependencies, Registry
Master Helm charts for Kubernetes: chart structure, templating, values, dependencies, hooks, libraries, registry management, and production patterns for deployment.
GuideGitHub Actions CI/CD: Workflows, Runners, Secrets
Master GitHub Actions for CI/CD: workflows, reusable workflows, composite actions, secrets management, runners, matrix builds, caching, and deployment patterns.
RecipeMount Configs and Secrets into Kubernetes Pods
How to mount ConfigMaps and Secrets into Kubernetes pods using env vars, volumes, projected volumes, and secret management with external secrets.
GuideComplete Guide to GitOps with ArgoCD
Deploy Kubernetes applications with GitOps using ArgoCD. Covers installation, ApplicationSets, sync strategies, Helm/Kustomize, RBAC, and multi-cluster management.
GuideComplete Guide to Kubernetes Ingress
Configure and troubleshoot Kubernetes Ingress controllers. Covers NGINX Ingress, TLS, path routing, annotations, IngressClass, and common pitfalls.