Kubernetes Deployment — ConfigMaps, Secrets, and Hot Reload
Embedded deployment
Section titled “Embedded deployment”The simplest Kubernetes deployment — gateway serves files directly:
apiVersion: apps/v1kind: Deploymentmetadata: name: frontendspec: replicas: 3 selector: matchLabels: app: frontend template: metadata: labels: app: frontend spec: containers: - name: app image: myapp:latest ports: - containerPort: 8080 envFrom: - configMapRef: name: frontend-public-config - secretRef: name: frontend-sensitive-config livenessProbe: httpGet: path: /rep/health port: 8080 initialDelaySeconds: 5 readinessProbe: httpGet: path: /rep/health port: 8080 initialDelaySeconds: 2
---apiVersion: v1kind: ConfigMapmetadata: name: frontend-public-configdata: REP_PUBLIC_API_URL: "https://api.example.com" REP_PUBLIC_FEATURE_FLAGS: "dark-mode" REP_PUBLIC_ENV_NAME: "production" REP_GATEWAY_STRICT: "true"
---apiVersion: v1kind: Secretmetadata: name: frontend-sensitive-configtype: OpaquestringData: REP_SENSITIVE_ANALYTICS_KEY: "UA-XXXXX-prod"Sidecar pattern (with nginx)
Section titled “Sidecar pattern (with nginx)”When you want nginx serving files with the gateway as a sidecar:
apiVersion: apps/v1kind: Deploymentmetadata: name: frontendspec: template: spec: containers: - name: nginx image: nginx:alpine volumeMounts: - name: static-files mountPath: /usr/share/nginx/html readOnly: true ports: - containerPort: 80
- name: rep-gateway image: ghcr.io/ruachtech/rep/gateway:latest args: - "--upstream=localhost:80" - "--port=8080" - "--strict" - "--env-file=/config/.env" - "--hot-reload" - "--hot-reload-mode=file_watch" - "--watch-path=/config" volumeMounts: - name: config-volume mountPath: /config readOnly: true ports: - containerPort: 8080 livenessProbe: httpGet: path: /rep/health port: 8080
initContainers: - name: copy-static image: myapp:latest command: ['cp', '-r', '/app/dist/.', '/static/'] volumeMounts: - name: static-files mountPath: /static
volumes: - name: static-files emptyDir: {} - name: config-volume configMap: name: frontend-dynamic-configThe mounted ConfigMap must hold a single key containing a whole .env file, because --env-file reads one env-format file:
apiVersion: v1kind: ConfigMapmetadata: name: frontend-dynamic-configdata: .env: | REP_PUBLIC_API_URL=https://api.example.com REP_PUBLIC_FEATURE_FLAGS=dark-mode REP_PUBLIC_ENV_NAME=productionHot reload with ConfigMaps
Section titled “Hot reload with ConfigMaps”Use file watch mode to detect ConfigMap changes without pod restarts:
- Mount the ConfigMap as a volume, with a single key holding a whole
.envfile - Point the gateway at it with
--env-file=/config/.env— this is what reload re-reads - Enable
--hot-reload --hot-reload-mode=file_watch --watch-path=/config - Update the ConfigMap:
kubectl apply -f configmap.yaml - Kubernetes swaps the volume’s
..datasymlink, changing the mount directory’s mtime - The gateway detects the change, re-reads the env file, and broadcasts via SSE
- Connected browsers receive the update
Variables passed through envFrom alone are not hot-reloadable. Changing frontend-public-config or frontend-sensitive-config above requires a pod restart (kubectl rollout restart). To rotate a REP_SENSITIVE_* value without one, move it into the mounted env file — sourced from a Secret instead of a ConfigMap — since --env-file accepts a single path.
# Update a config valuekubectl patch configmap frontend-dynamic-config \ -p '{"data":{"REP_PUBLIC_FEATURE_FLAGS":"dark-mode,ai-assist"}}'Service
Section titled “Service”apiVersion: v1kind: Servicemetadata: name: frontendspec: selector: app: frontend ports: - port: 80 targetPort: 8080 type: ClusterIP