Runtime Environment Variables with React, Kubernetes, and Apache
Building React apps once and deploying them across multiple environments—without rebuilding—is a common challenge. Most frontend apps hardcode environment variables at build time, meaning any change (like an API URL or login redirect) requires a full rebuild and redeploy. That’s slow, error-prone, and breaks the Twelve-Factor App principle of separating config from code.
In this guide, I’ll show you how to configure React environment variables at runtime using Apache and Kubernetes, enabling you to use the same Docker image across dev
, staging
, and production
.
React Runtime Configuration with Kubernetes and Apache
The Twelve-Factor App: Config Principle
The Twelve-Factor App methodology emphasizes separating config from code. Your application shouldn’t contain hardcoded URLs, credentials, or environment flags. Instead, it should read its config at runtime—from environment variables, mounted files, or external services.
In Kubernetes, this usually means using ConfigMaps or Secrets, injected into the container at runtime. The frontend world (especially React) hasn’t caught up yet—but here’s how we make it work.
Step 1: Create a Runtime Config File with ConfigMap
Strip all .env
files from your React app. Instead, create a ConfigMap that injects a config.js
file containing your runtime variables:
apiVersion: v1
kind: ConfigMap
metadata:
name: my-application-config
namespace: my-namespace
data:
config.js: |
window.REACT_APP_API_URL="https://api.my-app.com";
window.REACT_APP_LOGIN_URL="https://my-app.com/login";
window.REACT_APP_REDIRECT_URL="https://my-app.com/redirect";
This file is a plain JS script that sets variables on the window
object.
Step 2: Inject Config into React’s Public HTML
In your public/index.html
, inject the config file using a <script>
tag:
<script src="%PUBLIC_URL%/config.js"></script>
This loads your variables before React runs. Now let’s make sure the app reads them safely.
Step 3: Abstract Config Access in React
In src/config.ts
, map and export your runtime variables:
const REACT_APP_API_URL: string = window.REACT_APP_API_URL || '';
const REACT_APP_LOGIN_URL: string = window.REACT_APP_LOGIN_URL || '';
const REACT_APP_REDIRECT_URL: string = window.REACT_APP_REDIRECT_URL || '';
export {
REACT_APP_API_URL,
REACT_APP_LOGIN_URL,
REACT_APP_REDIRECT_URL
};
Use these throughout your app like this:
import { REACT_APP_LOGIN_URL } from 'config';
<A href={REACT_APP_LOGIN_URL}>
Login
</A>
This keeps your code clean and avoids scattered window
references.
Step 4: Dockerfile with Apache Runtime
Here’s a multi-stage Dockerfile that builds your React app and serves it via Apache:
# Build stage
FROM node:18-alpine as builder
WORKDIR /app
COPY package.json ./
RUN npm install --silent
COPY . ./
RUN npm run build
# Runtime stage
FROM httpd:2.4-alpine
COPY --from=builder /app/build /usr/local/apache2/htdocs/my-app
EXPOSE 80
ENTRYPOINT ["httpd-foreground"]
You do not bundle config.js
here—it will be mounted at runtime via Kubernetes.
Step 5: Kubernetes Deployment with ConfigMap Mount
Here’s how you mount the config.js
file in your deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
namespace: my-namespace
spec:
replicas: 1
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-app
image: my-app:latest
ports:
- containerPort: 80
volumeMounts:
- name: config-js
mountPath: /usr/local/apache2/htdocs/my-app/config.js
subPath: config.js
volumes:
- name: config-js
configMap:
name: my-application-config
This replaces config.js
inside the Apache root with the runtime file.
Bonus: Local Development Tip
If you’re working locally, you can add a public/config.js
file with the same variables:
window.REACT_APP_API_URL="http://localhost:4000/api";
Just be sure to add it to .dockerignore
and .gitignore
so it doesn’t get bundled or committed.
Final Thoughts
Using runtime config for React apps lets you build once, run anywhere. It follows cloud-native and Twelve-Factor principles, simplifies deployments, and decouples infrastructure from application logic.
Whether you’re using Apache, NGINX, or another static file server, mounting a config.js
file via Kubernetes ConfigMap is the cleanest and most scalable solution.
Stop rebuilding. Start injecting.