Injecting Environment Variables into React Apps in Docker on Azure

Introduction

Environment-specific configuration is easy in backend systems. You use environment variables, and everything just works. But with frontend applications like React, which are built ahead of time and served statically in the browser, things get a bit trickier.

In this post, you’ll learn how to inject dynamic environment variables into a React application running inside a Docker container, specifically in a setup deployed to Azure Container Apps. You’ll see why the default approaches don’t work, and how to implement a flexible solution that doesn’t require rebuilding your frontend every time a config value changes.

The Problem: Static Frontend vs. Dynamic Config

React apps are compiled ahead of time. When you run npm run build, the output is a static bundle that includes all environment variables available at build time. Once deployed, your app in the browser has no way to read server-side variables — there’s simply no Node.js, no access to process.env, nothing.

So what happens when you need to inject something dynamic, like the URL of a backend API or an image CDN endpoint, and you want that to vary per deployment environment?

You could rebuild the app with different .env files, but that quickly gets tedious — especially in a containerized cloud environment like Azure.

The Solution: env.js to the Rescue

Instead of trying to jam dynamic values into the React build, we inject them at runtime. The trick is to create a small JavaScript file, say env.js, that sets a global window.env object. Here’s what it might look like:

window.env = {
  BACKEND_URL: 'https://my-backend-service.azurecontainerapps.io',
  IMAGE_ENDPOINT: 'https://somestorageaccount.blob.core.windows.net/myspecialcontent',
  WELCOME_MESSAGE: 'When you read this, while running in Azure Container apps, then the env.js file is not generated correctly with createenv.sh while starting the container.'
};

This file is included via a <script> tag in your index.html before your React bundle is loaded. Inside your app, you can then access window.env.BACKEND_URL and friends — fully dynamic, and updated per deployment.

Building env.js at Container Startup

To avoid rebuilding the container every time a config value changes, we generate the env.js file at container startup, using a simple shell script.

Here’s the script createenv.sh:

#!/bin/sh

# Set output path for env.js
FILE="/usr/share/nginx/html/env.js"

# Read values from container environment
VALUE_IMAGE_ENDPOINT="https://$STORAGE_ACCOUNT_NAME.blob.core.windows.net/$STORAGE_CONTAINER_NAME"
VALUE_WELCOME_MESSAGE=${WELCOME_MESSAGE:-"WELCOME_MESSAGE not set in environment"}
VALUE_BACKEND_URL=${BACKEND_URL:-"BACKEND_URL not set"}

# Write env.js
cat <<EOL > "$FILE"
window.env = {
  BACKEND_URL: '${VALUE_BACKEND_URL}',
  IMAGE_ENDPOINT: '${VALUE_IMAGE_ENDPOINT}',
  WELCOME_MESSAGE: '${VALUE_WELCOME_MESSAGE}'
}
EOL

echo "env.js written to $FILE"

This script is executed via an entrypoint.sh script:

#!/bin/sh

echo "Running entrypoint.sh..."
./app/createenv.sh
exec "$@"

Dockerfile

Here’s the full Dockerfile that uses a multi-stage build:

FROM node:20 AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm install
COPY . .
RUN npm run build
RUN apt-get update &amp;&amp; apt-get install -y dos2unix
RUN dos2unix ./createenv.sh

FROM nginx:stable-alpine
COPY nginx.conf /etc/nginx/conf.d
COPY --from=build /app/dist /usr/share/nginx/html
EXPOSE 80
COPY --from=build ./app/createenv.sh ./app/createenv.sh
RUN chmod +x ./app/createenv.sh
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["sh", "/entrypoint.sh"]
CMD ["nginx", "-g", "daemon off;"]

NGINX Config

Nothing fancy here — just the usual single-page app fallback:

server {
    listen 80;
    server_name localhost;

    root /usr/share/nginx/html;
    index index.html;

    location / {
        try_files $uri $uri/ /index.html;
    }
}

How the React App Accesses window.env

In your React app, you can safely read values like so:

const backendUrl = window.env?.BACKEND_URL;

You can even create a small utility wrapper:

export const config = {
  backendUrl: window.env?.BACKEND_URL,
  imageEndpoint: window.env?.IMAGE_ENDPOINT,
};

Just make sure env.js is loaded before your React app.

Azure Container Apps: Setting Env Vars

When deploying to Azure, environment variables can be configured in the Azure Portal under your Container App > Containers > Environment Variables, or via CLI:

az containerapp update \
  --name my-react-app \
  --resource-group my-group \
  --set-env-vars BACKEND_URL=https://api.example.com STORAGE_ACCOUNT_NAME=mystorage STORAGE_CONTAINER_NAME=public

These values will be picked up by createenv.sh at container start.

Wrap-up & Takeaways

  • React apps are static and can’t read server env vars directly
  • To inject dynamic config, generate a global env.js at container startup
  • Use a shell script and a lightweight entrypoint setup
  • Works great with Azure Container Apps and Docker
  • No rebuilds needed when config values change

Call to Action

Are you doing something similar — or maybe even cooler — in your frontend deployments? I’d love to hear about it. Share your ideas or problems on GitHub or hit me up on oliverscheer.tech!

Kommentar verfassen

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Nach oben scrollen