Running React on Docker with nginx

Seeing modern day applications often store their state in databases or caches, horizontally scaling your web applications with technologies such as Docker becomes increasingly feasible.

In this article, we'll set up a (very simple) React application, and create a Docker container that can build our application, and run it in conjunction with nginx.

You can find the code used in this article on GitHub.

What you will need:

  • Recent version of Docker - at the time of writing, we used Docker CE 17.09
  • Recent version of both Nodejs and npm

Once you've installed those, and you've fired up your favourite editor, we're good to go!

Getting Started

What we're looking to do is create a single Dockerfile that will take our application's source, compile it, and then create a Docker image based on nginx to serve our application.

To get there, first we'll need something resembling a React app to deploy. You can either grab the one from the sample code, or use the create-react-app package to bootstrap a new, empty React app for you:

npx create-react-app sample

Note that npx isn't available in all versions of npm - if it doesn't work, try running:

npm install -g create-react-app
create-react-app my-app

NPM will do its thing, and you'll end up with a sample directory containing a couple of files, along with a src, public and node_modules folder. We'll be using this directory as our working directory for the rest of this article.

Builder

Now that we've got a React app, we can build it. You can try building it locally first by cding to your workspace directory and running

npm run build

This'll create a production build of your application which you can then serve by running serve -s build. While this is fine for running things locally, ideally we want to use a more robust web server like nginx or Apache to serve our application in a production scenario.

We'll be creating a single Dockerfile that contains both our builder image and the nginx image that will end up serving our application. In your app's directory, create a Dockerfile with the following contents:

This will grab the node image, copies our local working directory onto the image, and create a production ready build with all the artifacts we'll need to run our React app in production.

Optionally, you can try building the Docker image by running

docker build -t react-sample-app .

To see if everything up to this point is working properly. If it does, we can continue to the next bit: getting nginx set up to serve our app.

Nginx

Now that we have a production ready build of our React app, we can start serving it via nginx.

We will need to let nginx know how we want to serve our application. We do this by creating a default.conf pointing nginx at the directory we'll copy our React app to. Create a folder called nginx in your workspace directory, and inside it, create a default.conf file with the following contents:

Next we'll extend the Dockerfile we created in the previous step to grab the contents of the build directory and move it to the nginx image:

Note the COPY --from=builder instruction we use, which indicates that we want to grab the build folder from the builder image. We'll move that folder, along with the nginx configuration file over to our final nginx image.

Building the final image

Previously, we have configured both the builder image to build our React app, and the nginx image which will end up serving it. All that's left, is building the final image we can run on our production environment.

In your working directory, simply run:

docker build -t react-sample-app .

Yes, that's the same command we ran before to build our application in a Docker container - except this time it bakes in the nginx hosting layer as well.

After that's completed, you'll end up with a react-sample-app image containing our React app and nginx. You can see if it runs by running:

docker run -p 8080:80 react-sample-app

Where 8080 is the port on your machine, and 80 is the port inside the container.

If all went well, you should be able to see your application running on http://localhost:8080.

Again, if you need it, you can find a working version of the code above over at GitHub.

Thanks for reading!