Runtime Config


https://kubernetes.io/docs/concepts/configuration/secret/
https://docs.docker.com/compose/how-tos/environment-variables/
https://docs.aws.amazon.com/eks/latest/userguide/manage-secrets.html
https://cloud.google.com/secret-manager/docs/secret-manager-managed-csi-component
https://learn.microsoft.com/en-us/azure/aks/csi-secrets-store-driver
https://external-secrets.io/latest/

1. Important Points#

configuration goal:
    same image
    different runtime config
    no secret in git
    no secret baked into image
    fail fast when config missing

rule:
    non-secret -> env / ConfigMap
    secret -> Secret / external secret manager / mounted file

2. Application Config Loader#

import "dotenv/config";
import { existsSync, readFileSync } from "node:fs";
import { z } from "zod";

const configSchema = z.object({
  NODE_ENV: z.enum(["local", "dev", "test", "staging", "prod"]).default("local"),
  PORT: z.coerce.number().int().positive().default(3000),
  LOG_LEVEL: z.enum(["debug", "info", "warn", "error"]).default("info"),
  DATABASE_URL: z.string().url(),
  JWT_SECRET: z.string().min(32)
});

function readSecret(name) {
  const filePath = process.env[`${name}_FILE`];

  if (filePath !== undefined && existsSync(filePath)) {
    return readFileSync(filePath, "utf8").trim();
  }

  return process.env[name];
}

const parsed = configSchema.safeParse({
  ...process.env,
  JWT_SECRET: readSecret("JWT_SECRET")
});

if (!parsed.success) {
  console.error(parsed.error.flatten().fieldErrors);
  process.exit(1);
}

export const config = parsed.data;
supports:
    JWT_SECRET
    JWT_SECRET_FILE

3. Local#

NODE_ENV=local
PORT=3000
LOG_LEVEL=debug
DATABASE_URL=postgres://order:password@localhost:5432/order
JWT_SECRET=change-me-change-me-change-me-change-me

4. Docker#

docker run --rm \
  -p 3000:3000 \
  --env-file .env.local \
  order-api:local
docker run --rm \
  -p 3000:3000 \
  -e JWT_SECRET_FILE=/run/secrets/jwt_secret \
  -v "$PWD/secrets/jwt_secret:/run/secrets/jwt_secret:ro" \
  --env-file .env.local \
  order-api:local

5. Kubernetes#

apiVersion: v1
kind: ConfigMap
metadata:
  name: order-api-config
data:
  NODE_ENV: prod
  PORT: "3000"
  LOG_LEVEL: info
  DATABASE_URL: postgres://order:password@postgres:5432/order
apiVersion: v1
kind: Secret
metadata:
  name: order-api-secret
type: Opaque
stringData:
  JWT_SECRET: change-me-change-me-change-me-change-me
envFrom:
  - configMapRef:
      name: order-api-config
  - secretRef:
      name: order-api-secret

6. AWS / EKS#

options:
    Kubernetes Secret
    AWS Secrets Manager + Secrets Store CSI Driver
    External Secrets Operator
    app reads Secrets Manager directly

recommendation:
    use IRSA / Pod Identity
    grant least privilege

7. GCP / GKE#

options:
    Kubernetes Secret
    Google Secret Manager CSI
    External Secrets Operator
    app reads Secret Manager directly

recommendation:
    use Workload Identity
    avoid static service account keys

8. Azure / AKS#

options:
    Kubernetes Secret
    Azure Key Vault CSI Driver
    External Secrets Operator
    app reads Key Vault directly

recommendation:
    use managed identity / workload identity

9. Alibaba Cloud / ACK#

options:
    Kubernetes Secret
    KMS / Secret Manager integration
    External Secrets Operator when provider support is available
    app reads KMS Secrets Manager directly

10. Vault#

options:
    Vault Agent Injector
    Vault Secrets Operator
    External Secrets Operator
    app reads Vault directly

11. Decision#

Runtime Simple Better For Production
Local .env.local local secret manager
Docker --env-file mounted secret
K8S ConfigMap + Secret CSI / External Secrets
EKS Kubernetes Secret AWS Secrets Manager
GKE Kubernetes Secret Google Secret Manager
AKS Kubernetes Secret Azure Key Vault
ACK Kubernetes Secret KMS integration
Vault mounted file Vault Agent / VSO

12. Production Checklist#

application:
    validate config at startup
    support env and *_FILE
    fail fast on missing secret
    do not log secret values

container:
    no .env in image
    runtime injection only

kubernetes:
    ConfigMap for non-secret
    Secret/external secret for sensitive values
    restrict RBAC