Stage 7 · Master
Containerization and Deployment
ConfigMaps, Secrets, and Resource Limits
How runtime configuration should be separated from images, why Secrets are different from ordinary settings, and how Meridian should size pods from evidence instead of tutorial defaults.
Why Runtime Configuration Exists
A production image should be promotable across environments without recompilation. That means values that differ by environment cannot be baked into the image. Ports, downstream URLs, feature flags, database credentials, Redis passwords, and external API keys are deployment-time concerns. Code defines what a service can do; runtime configuration decides where and under which credentials it does it.
Kubernetes splits runtime data into two broad categories. ConfigMaps hold non-secret configuration such as ports, hostnames, and log levels. Secrets hold material whose casual disclosure widens access or damages trust: passwords, tokens, certificates, and signing keys. The distinction is not cosmetic. It affects access control, operational handling, and the probability of values leaking through logs, shell history, or copy-pasted manifests.
ConfigMaps vs Secrets
| Category | Typical examples | Handling rule |
|---|---|---|
| ConfigMap | LOG_LEVEL, GATEWAY_PORT, downstream service URLs, feature toggles | Safe to review openly inside the engineering team; still versioned carefully because bad config can cause outages. |
| Secret | POSTGRES_PASSWORD, REDIS_PASSWORD, JWT signing material, API tokens | Limit read access, avoid echoing in tooling, rotate deliberately, and never commit production values to source control. |
| Image build argument | Compiler settings or build metadata only | Do not use for environment-specific runtime values. Rebuilding per environment destroys artifact immutability. |
What Meridian Already Knows
The existing .env.example in meridian already separates several useful concepts. It defines GATEWAY_PORT=8080, IDENTITY_SERVICE_PORT=8081, COMMUNITY_SERVICE_PORT=8082, BILLING_SERVICE_PORT=8083, shared LOG_LEVEL, local downstream URLs for the gateway, PostgreSQL development defaults, and a Redis development password. That file is appropriate for local scaffolding because the values are fake development credentials. It is not an acceptable production secret-management strategy.
Kubernetes Secret objects are often rendered as base64-encoded data. Base64 is an encoding format, not encryption. Real protection comes from access control, external secret sources, rotation practice, and keeping raw secret values out of routine operational output.
Planned Runtime Objects
deploy/k8s/base/gateway/configmap.yamlcreateResponsibility: Stores non-secret gateway settings such as LOG_LEVEL and the in-cluster downstream base URLs.
Why now: apps/gateway/internal/config/config.go already models these values; Kubernetes must inject environment-specific values without rebuilding the image.
deploy/k8s/base/identity-service/configmap.yamlcreateResponsibility: Stores identity-service runtime settings such as listen port and future database hostnames.
Why now: Identity concerns need their own configuration boundary rather than inheriting every variable in the cluster.
deploy/k8s/base/community-service/configmap.yamlcreateResponsibility: Stores community-service runtime settings.
Why now: Per-service config keeps blast radius small and review intent clear.
deploy/k8s/base/billing-service/configmap.yamlcreateResponsibility: Stores billing-service runtime settings.
Why now: Billing will eventually need different external dependencies and feature flags from the other services.
deploy/k8s/base/platform-secrets.yamlcreateResponsibility: Defines credential material such as PostgreSQL and Redis passwords, ideally sourced from an external secret manager.
Why now: Production passwords must not live in .env files committed to git, even as examples copied forward by habit.
deploy/k8s/base/gateway/deployment.yamlmodifyResponsibility: Consumes gateway ConfigMap entries and only the Secrets the gateway truly needs.
Why now: A service should receive the minimum runtime data required for its own process, not the union of every platform secret.
Resource Requests and Limits
Resource requests tell the scheduler what a pod normally needs. Resource limits cap what it may consume. Requests are about placement honesty; limits are about containment. Both are necessary, and both are commonly abused. A request that is too small causes the scheduler to overpack nodes. A memory limit that is too small causes routine traffic spikes to turn into OOMKills. Copy-pasting the same values into every service because all four happen to be written in Go is not capacity planning.
| Decision | Naive shortcut | Production-grade rule |
|---|---|---|
| CPU request | Choose one tiny value for every service | Start from measurement and expected concurrency. gateway and billing-service do not have identical CPU behavior. |
| Memory limit | Set it just above idle usage to look efficient | Leave enough headroom for Go heap growth, burst traffic, and garbage-collection cycles. |
| Secret distribution | Mount one giant secret into every pod | Split by service and rotation boundary so a billing-only credential change does not restart the entire platform. |
Example Specification
apiVersion: v1
kind: ConfigMap
metadata:
name: gateway-config
namespace: meridian
data:
GATEWAY_PORT: "8080"
LOG_LEVEL: info
IDENTITY_SERVICE_URL: http://identity-service:8081
COMMUNITY_SERVICE_URL: http://community-service:8082
BILLING_SERVICE_URL: http://billing-service:8083
---
apiVersion: v1
kind: Secret
metadata:
name: platform-secrets
namespace: meridian
type: Opaque
stringData:
POSTGRES_PASSWORD: change-me-outside-git
REDIS_PASSWORD: change-me-outside-git
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: gateway
namespace: meridian
spec:
template:
spec:
containers:
- name: gateway
image: ghcr.io/thesyscoder/meridian/gateway:sha-PLACEHOLDER
envFrom:
- configMapRef:
name: gateway-config
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 256MiThe numeric resource values here are starting hypotheses, not authoritative production settings. Real values should be adjusted after measurement. The concept being taught is the split between immutable image, runtime configuration, and bounded resource policy.
Mark this lesson complete to store local progress and unlock a cleaner resume path the next time you visit.