Handbooks / Microservices / Chapter 7
Configuration and Feature Flags
7 pages · ~5 min✓ Reviewed
Builds on Securing Microservices. Next up: Observability and Deployment.
Externalized Config and the 12-Factor Model
1. What Counts as Config
- Config = anything that varies between deploys
- Examples: DB URL, credentials, hostnames, API keys
- Code does not vary, so keep the two separate
2. 12-Factor Rule III
- Build onceone artifact
- Devenv config
- Stagingenv config
- Prodenv config
- Store config in the environment, never in the codebase
- Artifact is promoted unchanged through every stage
| application-prod.yml | Env vars | |
|---|---|---|
| Grows with | every new environment | nothing: orthogonal |
| Grouped by | environment name | never |
| Lives in | the repo | the deploy |
3. Prove the Separation
- Litmus test: could you open-source the repo now without leaking a credential?
- If not, config is leaking into code
- Prove it: litmus test plus a secret scan of the repo
- Why one build? The tested artifact is what ships
git grep -iE "password|secret|api_key"any hit with a real value means a leak
Per-environment files as the main mechanism multiply with every new environment.
Env Vars, Config Servers and Precedence
1. Env Vars vs Config Servers
| Env vars | Config server | |
|---|---|---|
| Plus | Simple, any language, everywhere | Central, versioned, per-app/profile |
| Minus | Flat strings, no audit, in /proc | Needs a server |
| Update | Restart needed | Dynamic refresh |
| Tools | Any runtime | Spring Cloud Config, Consul KV, etcd, AppConfig, SSM |
2. Precedence and Binding
- CLI flagswins
- Env vars
- Config server
- File
- Defaultsloses
- Know your framework's order
- Rule of thumb: closest to the process wins
- Spring Boot: remote properties rank below env vars and CLI args
- Env var names map to property keys (relaxed binding)
export SPRING_DATASOURCE_URL=jdbc:postgresql://db/app
binds to spring.datasource.url
3. Startup Checks and Mistakes
Kubernetes ConfigMaps and Secrets
1. ConfigMap and Secret
- ConfigMap: non-sensitive key/values or whole files
- Secret: sensitive data, same shape, values base64 in
data - base64 is encoding, not encryption
kubectl create configmap app-cfg --from-literal=LOG_LEVEL=info --from-file=app.properties kubectl create secret generic db --from-literal=password=s3cret
create both from the CLI
2. Consuming and Updates
| Way | How | Updates |
|---|---|---|
| env | valueFrom.configMapKeyRef / secretKeyRef | fixed at start |
| envFrom | imports all keys | fixed at start |
| volume | each key becomes a file | refreshes eventually |
- Volume refresh waits on the kubelet sync; subPath mounts never refresh
- Env change needs
kubectl rollout restart deploy/app
Feature Flags: Release vs Ops
1. Flag Types
- Feature flag = runtime conditional: code ships dark, a toggle releases it
- Decouples deploy from release, no redeploy
| Type | Lifetime | Use |
|---|---|---|
| Release | short | hides unfinished work |
| Experiment | short | A/B test |
| Ops | long | kill switch |
| Permission | very long | entitlements |
2. Release vs Ops
- Release flags: delete at 100% rollout
- Ops flags: permanent, on-call sheds load or disables a failing dependency
- Target by user ID, segment or percentage
- Hash user ID + flag key: sticky variant
if (flags.isEnabled('new-checkout', user)) { newCheckout() } else { oldCheckout() }
Dark Launches, Progressive Rollout and Flag Debt
1. Dark Launch
- Dark launch: new path runs in prod on real traffic, no user-visible effect
- Shadow traffic or mirrored reads: compare responses, discard result
- Never mirror writes with side effects
2. Progressive Rollout
- 1Staff
- 21%
- 35%
- 425%
- 550%
- 6100%
- Watch error rate and latency at each step
- Roll back by flipping the flag, not redeploying
3. Dark vs Canary vs Blue/Green
| Dark launch | Canary | Blue/green | |
|---|---|---|---|
| Idea | Hidden from users | Small slice of real users | Swap whole environments |
| Level | Feature | Deploy | Deploy |
4. Flag Debt and Cleanup
Config and Flags Cheat Sheet
1. Config Rules and Precedence
- Config lives in env or a config store, never in code
- One artifact, many deploys
- Fail fast on missing keys
- Never log secrets
- CLI flagshighest
- Env vars
- Config server
- File
- Defaultslowest
2. Kubernetes
- ConfigMap = plain data; Secret = base64, not encrypted
- Env vars fixed at start:
kubectl rollout restart deploy/app - Volumes refresh, except subPath mounts
- Set
immutable: trueand enable etcd encryption
3. Flag Types and Lifetime
| Type | Lifetime | Note |
|---|---|---|
| Release | short | delete at 100% |
| Experiment | short | A/B test |
| Ops |
Check yourself
In Spring Boot, application.properties sets server.port=8080, the environment sets SERVER_PORT=9090, and the app starts as shown. Which port does it listen on?
- Answer: 7070.
- CLI args beat env vars, which beat file and server values.
# application.properties: server.port=8080 export SERVER_PORT=9090 java -jar app.jar --server.port=7070
Spot the bug: this gives a 25% rollout. Why does a user flip between variants?
- Answer: it is random per request, so there is no stable key.
- Hash user ID + flag key instead, so each user is sticky.
if (random() < 0.25) newCheckout()
A pod reads LOG_LEVEL from a ConfigMap through an env var. You edit the ConfigMap to debug, but the pod still logs at info. Why, and what fixes it?
- Answer: env vars are fixed at container start.
- Run
kubectl rollout restart deploy/appto pick up the new value.