Contents
- ConfigMap — Creating & Using
- Injecting as Environment Variables
- Injecting as Volume-Mounted Files
- Kubernetes Secrets
- Spring Cloud Kubernetes Config
- Auto-Refresh with @RefreshScope
- External Secrets Operator (ESO)
- Secrets Best Practices
The YAML below shows the complete configuration for this feature. Adjust the values to match your environment.
# configmap.yaml — non-sensitive application config
apiVersion: v1
kind: ConfigMap
metadata:
name: payment-service-config
namespace: payments
data:
# Individual key-value pairs
LOG_LEVEL: "INFO"
FEATURE_FLAG_NEW_CHECKOUT: "true"
MAX_RETRY_ATTEMPTS: "3"
# Full application.properties file as a multi-line value
application.properties: |
spring.application.name=payment-service
management.endpoints.web.exposure.include=health,info,prometheus
spring.jpa.show-sql=false
app.payment.timeout-seconds=30
# Create from literal values
kubectl create configmap payment-service-config \
--from-literal=LOG_LEVEL=INFO \
--from-literal=MAX_RETRY_ATTEMPTS=3 \
-n payments
# Create from an existing properties file
kubectl create configmap payment-service-config \
--from-file=application.properties=./k8s/application-prod.properties \
-n payments
# View
kubectl get configmap payment-service-config -n payments -o yaml
The YAML below shows the complete configuration for this feature. Adjust the values to match your environment.
# In the Deployment container spec
spec:
containers:
- name: payment-service
image: myorg/payment-service:1.0.0
env:
# Single key from ConfigMap
- name: LOG_LEVEL
valueFrom:
configMapKeyRef:
name: payment-service-config
key: LOG_LEVEL
# All keys from ConfigMap as env vars (SCREAMING_SNAKE_CASE → spring.property)
envFrom:
- configMapRef:
name: payment-service-config
Spring Boot automatically maps environment variables to properties: SPRING_DATASOURCE_URL → spring.datasource.url.
Mount a ConfigMap as a file — Spring Boot's spring.config.location picks it up automatically. Useful for full application.properties or YAML files.
spec:
containers:
- name: payment-service
image: myorg/payment-service:1.0.0
env:
- name: SPRING_CONFIG_LOCATION
value: "classpath:/,file:/etc/config/"
volumeMounts:
- name: app-config
mountPath: /etc/config
readOnly: true
volumes:
- name: app-config
configMap:
name: payment-service-config
items:
- key: application.properties
path: application.properties
Volume-mounted ConfigMap files are automatically updated by kubelet when the ConfigMap changes (within ~60 seconds) — without pod restart. Environment variables are NOT updated — they are fixed at pod startup.
The YAML below shows the complete configuration for this feature. Adjust the values to match your environment.
# secret.yaml — base64-encoded values (use Sealed Secrets or ESO in production)
apiVersion: v1
kind: Secret
metadata:
name: payment-service-secrets
namespace: payments
type: Opaque
data:
# Values must be base64-encoded
DB_PASSWORD: cGFzc3dvcmQxMjM= # echo -n "password123" | base64
STRIPE_API_KEY: c2tfbGl2ZV8uLi4=
stringData:
# stringData is plain text — Kubernetes base64-encodes it
REDIS_PASSWORD: "my-redis-password"
# Create from literals (Kubernetes does the base64 encoding)
kubectl create secret generic payment-service-secrets \
--from-literal=DB_PASSWORD=password123 \
--from-literal=STRIPE_API_KEY=sk_live_... \
-n payments
# Create TLS secret
kubectl create secret tls api-tls-cert \
--cert=tls.crt \
--key=tls.key \
-n payments
# Mount secrets as environment variables
spec:
containers:
- name: payment-service
envFrom:
- secretRef:
name: payment-service-secrets
# Or individual secret keys
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: payment-service-secrets
key: DB_PASSWORD
Spring Cloud Kubernetes reads ConfigMaps and Secrets directly from the Kubernetes API — no environment variable mapping needed. The ConfigMap name matches spring.application.name by default.
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-kubernetes-client-config</artifactId>
</dependency>
# application.yml
spring:
application:
name: payment-service # ConfigMap named "payment-service" is loaded automatically
cloud:
kubernetes:
config:
enabled: true
name: payment-service-config # override name
namespace: payments
secrets:
enabled: true
name: payment-service-secrets
namespace: payments
# RBAC — give the pod permission to read ConfigMaps and Secrets
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: config-reader
namespace: payments
rules:
- apiGroups: [""]
resources: ["configmaps", "secrets", "pods"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: config-reader-binding
namespace: payments
subjects:
- kind: ServiceAccount
name: default
namespace: payments
roleRef:
kind: Role
name: config-reader
apiGroup: rbac.authorization.k8s.io
Spring Cloud Kubernetes can watch ConfigMaps for changes and trigger a context refresh without restarting the pod.
# application.yml
spring:
cloud:
kubernetes:
reload:
enabled: true
mode: polling # or "event" for watch-based (requires RBAC watch verb)
period: 15000 # poll every 15 seconds
strategy: refresh # refresh vs restart_context vs shutdown
// Beans annotated with @RefreshScope are recreated after a reload
@RefreshScope
@Service
public class FeatureFlagService {
@Value("${feature.new-checkout:false}")
private boolean newCheckoutEnabled;
public boolean isNewCheckoutEnabled() { return newCheckoutEnabled; }
}
The External Secrets Operator syncs secrets from AWS Secrets Manager, HashiCorp Vault, GCP Secret Manager, or Azure Key Vault into Kubernetes Secrets — keeping the source of truth outside the cluster.
# SecretStore — points to AWS Secrets Manager
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
name: aws-secrets-manager
namespace: payments
spec:
provider:
aws:
service: SecretsManager
region: us-east-1
auth:
serviceAccount: # use IRSA for RBAC
name: external-secrets-sa
---
# ExternalSecret — defines which secrets to sync
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: payment-service-secrets
namespace: payments
spec:
refreshInterval: 1h
secretStoreRef:
name: aws-secrets-manager
kind: SecretStore
target:
name: payment-service-secrets # resulting K8s Secret name
creationPolicy: Owner
data:
- secretKey: DB_PASSWORD # key in the K8s Secret
remoteRef:
key: payment-service/prod # path in AWS Secrets Manager
property: db_password # field within the secret JSON
- secretKey: STRIPE_API_KEY
remoteRef:
key: payment-service/prod
property: stripe_api_key
- Never commit secrets to Git — even base64-encoded K8s secret YAML. Use Sealed Secrets or the External Secrets Operator.
- Enable encryption at rest — configure EncryptionConfiguration in your cluster to encrypt Secrets in etcd.
- Least privilege RBAC — only grant get on specific secrets; never list or watch all secrets cluster-wide.
- Mount as files, not env vars — secrets in env vars appear in crash dumps and kubectl describe pod output. File-mounted secrets are less exposed.
- Rotate regularly — use ESO's refreshInterval to automatically pull rotated secrets from Vault/SM without redeploying.
- Audit access — enable Kubernetes audit logging for secrets get operations to detect unexpected access.