DEV Community

Cover image for Frontend Runtime Environment Configuration Injection with Docker
Rasel Mahmud
Rasel Mahmud

Posted on

Frontend Runtime Environment Configuration Injection with Docker

Why Do We Need This?

Most SPA (Single Page Application) frameworks like React, Vite, Vue, or Angular read environment variables during build time from .env .env.production .env.development .env.staging .env.local etc files.

When the frontend is built, these values become part of the generated static assets.

Because of this, many teams rebuild the frontend separately for each environment:

  • Development
  • UAT
  • Staging
  • Production

This introduces unnecessary operational overhead:

  • multiple Docker images
  • repeated frontend rebuilds
  • harder version management

In many cases, the only thing changing between environments is the configuration itself.


Another Common Approach

Some teams build the frontend inside the Docker container and serve it using Nginx or another web server. This solves the hardcoded build-time environment issue, but introduces new operational overhead.

Problems

  • slower container startup
  • larger Docker images
  • longer deployment time
  • source code remains inside the image during build
  • even small environment changes require rebuilding the frontend
  • restarting a pod or Docker container may unnecessarily trigger another build process

In modern deployment systems, rebuilding the same frontend multiple times just to change API URLs or runtime configs is unnecessary overhead.

A better approach is:

  • build once
  • inject configuration during runtime

This makes deployments faster, cleaner, and easier to maintain.

Why Frontend Env Works Differently Than Backend

Backend applications like Node.js, Golang, or Python run directly on the server, so they can read environment variables dynamically during runtime using:

process.env
Enter fullscreen mode Exit fullscreen mode

But frontend frameworks like React, Vite, Vue, or Angular work differently.

They are built into static files like:

index.html
main.js
style.css
Enter fullscreen mode Exit fullscreen mode

During the build process, environment variables get embedded into these files.

After the build:

  • .env files are no longer used
  • process.env does not exist in the browser
  • changing API URLs or configs usually requires rebuilding the frontend

That’s why runtime configuration injection is very useful for modern frontend deployments.

The Solution: Runtime Configuration Injection

Instead of injecting environment variables during build time, inject them during runtime.

This allows the frontend build to remain completely environment-independent while configuration is loaded dynamically when the container starts.

This approach gives us:

  • a single reusable frontend build
  • faster deployments
  • cleaner CI/CD pipelines
  • easier environment management
  • flexible infrastructure configuration

It also reduces accidental exposure because configuration values are no longer permanently embedded inside compiled assets.


How It Works

Load a runtime configuration file before the frontend application starts.

Example:

<script src="/client.secret.js"></script>
Enter fullscreen mode Exit fullscreen mode

SPA entry HTML:

<!doctype html>
<html lang="en">
<head>
  <meta charset="UTF-8" />

  <!-- Runtime configuration -->
  <script src="/client.secret.js"></script>
</head>

<body>
  <div id="root"></div>

  <!-- Frontend application -->
  <script type="module" src="/src/main.tsx"></script>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

The frontend application can then access runtime values from:

window.__ENV__
Enter fullscreen mode Exit fullscreen mode

Runtime Config Example

Example: client.secret.js

window.__ENV__ = {
  API_BASE_URL: "https://api.example.com",
  APP_ENV: "production",
  FEATURE_FLAG: true
}
Enter fullscreen mode Exit fullscreen mode

Usage:

window.__ENV__
Enter fullscreen mode Exit fullscreen mode

Some teams store config in different window locations for light obfuscation:

window.__proto__.config = {
  API_BASE_URL: "https://api.example.com"
}
Enter fullscreen mode Exit fullscreen mode

Others use encrypted runtime strings:

window.__ENV__ = "encrypted-json-string"
Enter fullscreen mode Exit fullscreen mode

and decrypt them in the frontend.

However, this is not real security because the decryption logic and keys still exist in the browser.

These techniques only help with:

  • obfuscation
  • casual protection
  • reducing accidental exposure

Runtime config is best suited for:

  • API URLs
  • feature flags
  • environment names
  • public runtime configuration

Docker Runtime Injection

The runtime configuration file can be mounted dynamically during deployment.

Example:

services:
  frontend:
    image: registry.github.com/my-org/some-super-frontend:1.0.0
    volumes:
      - ./client.secret.js:/app/dist/client.secret.js
    ports:
      - "5173:5173"
Enter fullscreen mode Exit fullscreen mode

Benefits:

  • the same Docker image works across all environments
  • only runtime configuration changes
  • rebuilding becomes unnecessary

This works especially well with:

  • Docker
  • Kubernetes
  • Nginx
  • cloud-native infrastructure

Best Use Cases

This architecture works particularly well with:

  • React
  • Vite
  • Vue
  • Angular
  • static frontend deployments

and platforms such as:

  • Docker
  • Kubernetes
  • Nginx
  • CDN-based hosting

Final Thoughts

Modern frontend infrastructure is gradually shifting toward runtime-configurable frontend applications instead of environment-specific frontend builds.

Rather than rebuilding the same frontend multiple times for Development, UAT, Staging, and Production, teams can build once and inject configuration dynamically during deployment.

This approach provides several operational advantages:

  • faster deployments
  • smaller and cleaner CI/CD pipelines
  • reduced rebuild overhead
  • simpler version management
  • improved scalability
  • more flexible infrastructure configuration
  • cleaner Docker and Kubernetes workflows

while keeping deployment pipelines flexible and production friendly.

Top comments (0)