Running React application in Docker. With runtime configuration.

Running React application in Docker. With runtime configuration.

Create-react-app is great. So great that it has become a de-facto standard for creating new React applications. Docker is great as well. However it seems there’s no canonical way of creating a Docker image to run a create-react-app. Especially when you need to configure your React app on runtime. Let’s see how to do that.

The problem

The goals of the project are two fold:

  1. We want to compile a Create-react-app into Docker image and deploy on our infrastructure. The image shall contain compiled JavaScript, CSS files and other assets and be served by a web server.
  2. We want to configure the application on runtime. Imagine our application communicates with a REST API on backend. URL of the backend changes depending on what environment we are running in. Application running in production will use production API, application in staging will use staging API and so on. We want to compile the image only once and be able to deploy the same image in both environments.

To tackle the building problem we could use a two-step build process in our Dockerfile. In the first step we would use a build environment where we would copy our application files and build them. In the second step we would copy the compiled files to a new environment, install web server and have it serve the files.

Unfortunately this approach doesn’t provide us with much in terms of possibilities for runtime configuration. React application gets compiled into one static JavaScript file which is then sent from web server and executed in web browser on client’s side. There is just no easy way for us to insert runtime configuration once the file is compiled.

That’s why we are going to run the build process again when starting the container. We are basically going to copy our application files to the build environment and write a script which will first rebuild the application and then start the webserver. We will ensure the separation of source files and application files by copying them over to webserver’s document root.

The Solution

First the Dockerfile:

# Use build environment
FROM node:jessie as build-env
WORKDIR /app

# Install git and openssh in build environment, required by some packages in npm install
RUN apt-get update && \
apt-get install -y git openssh-client

# Install npm packages. Done in a separate step to have node_modules cached by Docker
COPY package.json ./
COPY package-lock.json ./
RUN npm install

# Copy source code
COPY . ./

# Build the application
RUN npm run build

# Build runtime image
FROM node:jessie

# Copy all files, including node_modules
WORKDIR /app
COPY --from=build-env /app /app

# Install npm in runtime environment
RUN apt-get update && \
apt-get install -y nginx

# Set run command to execute script
# The script will run npm build again, but using environment variables provided on runtime
CMD ["/bin/sh", "/app/docker_run.sh"]

Then we need the docker_run.sh script:

echo "Building package"
npm run build --production

echo "Copying files"
cp -R /app/build/* /var/www/html

echo "Starting nginx"
/usr/sbin/nginx -g "daemon off;"

Now we can start using environment variables in our application. We can use process.env.REACT_APP_API_URL anywhere in our React code and pass environment variable REACT_APP_API_URL when starting the Docker container.

 

 

Leave a Reply

Your email address will not be published. Required fields are marked *