Dockerizing Your React App

Dockerizing Your React App

The team at Facebook has made it very easy for someone to bootstrap a React application with their create-react-app CLI tool. You can literally have a fully-configured React application running and ready to add components within a minute or two. Things get a little more complicated when you want to deploy your app in a Docker container and adhere to 12-factor-app principles of using environment (ENV) variables to configure your app. Worry no more!

TL;DR

For the impatient folks who just need a solution, visit this Github repo I put together to demonstrate exactly how to add Docker and ENV variables to your React app.

Why is this important?

More companies (and individuals) are deploying their software within containers. Best practices adhere to the 12-factor-app methodology and in particular, avoiding hard-coding application configuration in your app itself. Since you can pass environment variables into your container at runtime, you can reuse the same image and change configuration of your application by passing different arguments as you spin up your instance.

Background

React apps bootstrapped with create-react-app include a development server, and preconfigured webpack settings to compile and produce a production deployment. When you deploy your app within Docker, you want the production build version. Unfortunately if you execute the npm run build --production command in your Docker image, then the environment variables you pass to the runtime container are not accessible.

Key challenges and solutions

  • React apps ignore ENV vars unless prefixed with REACT_APP_<var name>. I believe the only exception may be the NODE_ENV var, but others must be prefixed to be used in your app.
  • If you build your production app within your image, it will not recognize ENV vars at runtime (unless you pass them as build args during image creation). Because of this, your image should install all necessary dependencies and copy your source files, but your build and server start should be run in a script executed during the CMD step.

Example use case: change database host per environment

A popular use of ENV variables for application configuration is dynamically declaring a development database host, a test database host, and a production database host. You can simply change the environment variable for each environment and they all run the same app, but it connects to different databases. See: https://12factor.net/config

Using ENV variables within your app

const dbHost = process.env.REACT_APP_DB_HOST;

const dbConn = client.connect(dbHost); // illustrative purposes only

If you have several variables, I recommend declaring them all in a single file that you can import into other files and reference the variables. You can see an example of this in the Github repo link above.

Configuring your Docker image (Dockerfile) to run a script

FROM node:9.4

# Create app directory
WORKDIR /usr/src/app

# Expose port for service
EXPOSE 5000

# Install and configure `serve`.
RUN npm install -g serve

# Copy source code to image
COPY . .

# Install dependencies
RUN npm install

# Build app and start server from script
CMD ["/usr/src/app/run"]

Adding your run script (remember to make file executable permission)

#!/usr/bin/env bash

echo "===> Building ..."
npm run build --production

echo "===> Running ... "
exec serve -s build

The Github repo example adds more options, but the basics include building the app, and then starting the server to serve up the compiled version in the build directory.

Building and running your Docker image and changing configuration

# Assume in your app you connect to a database
# DEV:  set REACT_APP_DB_HOST=localhost
# TEST: set REACT_APP_DB_HOST=testdb.myco.com
# PROD: set REACT_APP_DB_HOST=proddb.myco.com

# build Docker image (one time)
docker build -t myimage .

# run local development instance
docker run --name myapp -p 8080:5000 \
-e REACT_APP_DB_HOST=localhost --rm myimage

# run testing instance
docker run --name myapp-test -p 8081:5000 \
-e REACT_APP_DB_HOST=testdb.myco.com --rm myimage

# run production instance
docker run --name myapp-prod -p 8082:5000 \
-e REACT_APP_DB_HOST=proddb.myco.com --rm myimage

Finally, as this example illustrates, you can build a single app and image once and reuse it. Each instance, depending on what environment variables you declare, determines what database the app connects to. If you visited port 8080, you would run the app on local database. Ports 8081 and 8082 respectively would all run against their respective databases. You can apply this same technique to configuring anything you wish in your app to behave differently each instance.

Conclusion

For those familiar with NodeJS/Express apps, the process.env syntax should be familiar. The tricks with passing environment variables for use in the app requires building the app at runtime via a script, and remembering to name your ENV variables with REACT_APP_ prefix. I hope this helps you "Dockerize" your React apps and adhere to 12-factor-app best practices for configuration.


Hi Mike, great write-up! I've been looking into a solution for runtime vs compile time env args using Docker and React. I stumbled upon your article after seeing your post referenced in a few places (links at bottom). While your post solves the problem, it also couples the build vs run stages of the image, since the webpack build happens on the host machine running the Docker image. This can be troublesome since a webpack build is often not a trivial process (depending on size of your JS app), and it's also not a pure "run" phase. Is this an issue that you've considered while designing your solution, and/or is there something that I'm overlooking? Cheers, Michael https://github.com/facebook/create-react-app/issues/982#issuecomment-393601963 https://stackoverflow.com/questions/49975735/rendering-an-environment-variable-to-the-browser-in-a-react-js-redux-production https://github.com/facebook/create-react-app/issues/578

Like
Reply
Zaky Rahim

Data Engineer | Making Robust Data System

7y

why you put database connection on a react app? shouldn't it be talked to REST/GraphQL ? opening db connection to the internet is dangerous.

Like
Reply

I came upon this article while searching for "12-factor-app methodology", but it answered a running problem that I've had: how to pass custom variables from docker to a CRA instance. I'm sure that I was using the wrong search terms before, but using the magical REACT_APP_ prefix should be more easy to find, I think. Thanks.

To view or add a comment, sign in

More articles by Mike Sparr

  • Notable Changes In Kubernetes 1.18 CLI

    Recently I decided to pursue the CKA (Certified Kubernetes Administrator) certification. Although using [k8s] in…

  • Useful Standards For Solution Design

    Many tutorials I see online are designed to get people adopting some tool or framework quickly, yet lack the discipline…

  • Kubernetes, Helm, Istio, Prometheus, Jaeger, and Grafana on a Macbook

    Settling into my hotel in San Francisco for the week and I decided to "geek out" this evening and spin up a Kubernetes…

  • Bay Area "Magic" From Montana

    Yesterday marked my last official day at ClientHub, and next week signals the beginning of a new challenge at Capsilon.…

    3 Comments
  • Explaining Blockchain and Crypto To A Five Year Old (ELI5)

    A coworker was searching for a good ELI5 (explain like I'm five) definition of how Blockchain and crypto works. We…

  • Are You Benchmarking Your Apps?

    I've written about the benefits of automation when it comes to DevOps, and test-driven-development when it comes to…

  • Finding our "why" at ClientHub & Reazo

    Just over a year ago I decided to join forces with another real estate technology startup, and consolidate our products…

    1 Comment
  • Modern Software Startups

    What series of publications on the Internet today doesn't benefit from photos of cats? As the saying goes, there are…

    1 Comment
  • Are You Down With TDD?

    "Yeah you know me." Well perhaps Naughty By Nature didn't sing this song, but hopefully most professional software…

  • Authorization Modeling By Example

    This article is a continuation of my prior article on Security Models: Authentication and Authorization from last…

Others also viewed

Explore content categories