skip to Main Content

The goal I am trying to achieve is to build a docker image (with a react app within) that is using environment variables from the host.

Planned workflow:

  1. Build the docker image locally
  2. Upload the docker image
  3. Call command docker-compose up

I want the environment variable REACT_APP_SOME_ENV_VARIABLE of the system (where the image is hosted) to be usable by the react app.

Current solution:

// App.js
function App() {
  return (
    <p>SOME_ENV_VARIABLE = {process.env.REACT_APP_SOME_ENV_VARIABLE}</p>
  );
}
# Dockerfile
FROM    node:13.12.0-alpine as build-step

# Install the app
RUN     mkdir /app
WORKDIR /app
COPY    package.json /app
RUN     npm install --silent

# Build the app
COPY    . /app
RUN     npm run-script build

# Create nginx server and copy build there
FROM    nginx:1.19-alpine
COPY    --from=build-step /app/build /usr/share/nginx/html

# docker-compose.yml
version: '3.5'

services:
  react-env:
    image:  react-env
    ports:
      - 80:80/tcp
    environment: 
      - REACT_APP_SOME_ENV_VARIABLE=FOO

What am I doing wrong and how do I fix it?

2

Answers


  1. Chosen as BEST ANSWER

    This was solved by using an NGINX docker package, inject the compiled React production code into the NGINX html folder, then modify the docker-entrypoint.sh file.

    FROM nginx:1.19-alpine
    COPY --from=build-step /app/build /usr/share/nginx/html
    COPY ./docker/docker-entrypoint.sh /docker-entrypoint.sh
    

    Then in that file add the following code at the end of the old script

    #!/bin/sh
    #!/bin/sh
    # vim:sw=4:ts=4:et
    
    set -e
    
    if [ -z "${NGINX_ENTRYPOINT_QUIET_LOGS:-}" ]; then
        exec 3>&1
    else
        exec 3>/dev/null
    fi
    
    if [ "$1" = "nginx" -o "$1" = "nginx-debug" ]; then
        if /usr/bin/find "/docker-entrypoint.d/" -mindepth 1 -maxdepth 1 -type f -print -quit 2>/dev/null | read v; then
            echo >&3 "$0: /docker-entrypoint.d/ is not empty, will attempt to perform configuration"
    
            echo >&3 "$0: Looking for shell scripts in /docker-entrypoint.d/"
            find "/docker-entrypoint.d/" -follow -type f -print | sort -n | while read -r f; do
                case "$f" in
                    *.sh)
                        if [ -x "$f" ]; then
                            echo >&3 "$0: Launching $f";
                            "$f"
                        else
                            # warn on shell scripts without exec bit
                            echo >&3 "$0: Ignoring $f, not executable";
                        fi
                        ;;
                    *) echo >&3 "$0: Ignoring $f";;
                esac
            done
    
            echo >&3 "$0: Configuration complete; ready for start up"
        else
            echo >&3 "$0: No files found in /docker-entrypoint.d/, skipping configuration"
        fi
    fi
    
    # Set up endpoint for env retrieval
    echo "window._env_ = {" > /usr/share/nginx/html/env_config.js
    
    # Collect enviroment variables for react
    eval enviroment_variables="$(env | grep REACT_APP.*=)"
    
    # Loop over variables
    env | grep REACT_APP.*= | while read -r line; 
    do
        printf "%s',n" $line | sed "s/=/:'/" >> /usr/share/nginx/html/env_config.js
        
        # Notify the user
        printf "Env variable %s' was injected into React App. n" $line | sed "0,/=/{s//:'/}"
    
    done
    
    # End the object creation
    echo "}" >> /usr/share/nginx/html/env_config.js
    
    echo "Enviroment Variable Injection Complete."
    
    
    exec "$@"
    
    

    Functionality:

    This will find all environment variable sent to the docker container running the frontend to extract all variables starting with REACT_APP and add them to a file named env_config.js.

    All you need to do in the react app is to load that script file, then access the environment variables using window._env_.<property>.

    DISCLAIMER Environment variables injected with this method is fully readable by anyone using the site. This is not a secure method for sensitive information. Only use this for things such as "where is the backend api endpoint" or other non-sensitive information that can be extracted just as easily.


  2. In your approach, the environment variables are injected when the container starts, and by that time your App is built and docker image is created, and also you cannot access process.env on client side. Therefore to access them on client side, we have to do the below steps.

    You must be using webpack in your React App for bundling and other stuff.

    So in you webpack.config.js, declare your environment variable REACT_APP_SOME_ENV_VARIABLE using Define plugin which will result in declaring the variables as global variables for the app.

    Your webpack config should look something like this:

    const path = require("path");
    const webpack = require("webpack");
    
    module.exports = {
      target: "web",
      performance: {
        hints: false,
      },
      node: {
        fs: "empty"
      },
      entry: "./src/index.js",
      output: {
        path: path.join(__dirname, "/build"),
        filename: "[name].[contenthash].js"
      },
      
      module: {
        rules: [
          //your rules
        ]
      },
    
      plugins: [
        new webpack.DefinePlugin({
          "envVariable": JSON.stringify(process.env.REACT_APP_SOME_ENV_VARIABLE),
        }),
      ],
    };
    

    And in your App, you can use the variable like this

    // App.js
    function App() {
      return (
        <p>SOME_ENV_VARIABLE = {envVariable}</p>
      );
    }
    

    NOTE: Make sure before RUN npm run-script build command is run, your environment variables are injected to docker container.
    For that you should declare your environment variables in DockerFile using ENV before RUN npm run-script build step.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search