Deploy your React App using Docker and Nginx
Published: 2023-11-27 17:26:29

Hello reader, it's Sourab here. Recently in one of my projects I used [Docker](https://docs.docker.com/) and [Nginx](https://docs.nginx.com/) for deploying a [React App](https://react.dev/) to a front-end server. Let's see how I did it.

First, we will need a React project so you can skip creating a new React project if you already have one. 

**Create a new React project and spin it up**  

```
npx create-react-app with-docker
cd with-docker
npm start
```

It will start your react app at port `3000` and open up in the browser with this interface below

![React start off interface](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k5v1ww88oqjo7af7jeg5.png)

Our app is running fine. So let's close it now and move to the next step.

**Create a Dockerfile**
In the root directory of your project, we will create a new file and name it `Dockerfile`. It will look something like this in your project directory

![Director shot](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yqitsm0fhe6y1mqwljzo.png)

and add this code to that Dockerfile

```
#React app image
FROM node:lts-alpine as build

WORKDIR /app

COPY package*.json ./

RUN npm ci

COPY . .

RUN npm run build
```
`FROM node:lts-alpine as build` will tell docker to download a slim LTS(Long Term Support) version of Node image from [Docker Hub](https://hub.docker.com/) and set up a Node environment with all base requirements needed to run a Node project in our case a React project for production that. 

`WORKDIR /app` will tell the docker that in that image we need a dedicated director i.e. `/app` where the whole project will sit. And whatever changes we make using the steps in this Dockerfile will only affect that `/app` directory nothing else.

`COPY package*.json ./` will copy all the files having the prefix `package` and suffix `.json` into the working directory.

`RUN npm ci` will install all the packages specified in the `package.json` file of our project into the image.

`COPY . .` will copy the source directory in the working directory.

`RUN npm run build` will execute the `npm run build` script from `package.json` which will then generate a build directory and all our code will be minified, streamlined, and bundled together into static HTML, CSS, and JavaScript files. This build is highly compressed, optimized, and ready for production. We will use this build to serve.

**Nginx Configuration**
In the root directory of our project, we will create one file named `nginx.conf` and paste this code:
```
http {

  include mime.types;

  set_real_ip_from        0.0.0.0/0;
  real_ip_recursive       on;
  real_ip_header          X-Forward-For;
  limit_req_zone          $binary_remote_addr zone=mylimit:10m rate=10r/s;

  server {
    listen 80;
    server_name localhost;
    root /proxy;
    limit_req zone=mylimit burst=70 nodelay;

    location / {
            root   /usr/share/nginx/html;
            index  index.html index.htm;
            try_files $uri /index.html;   
        }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
  }
}

events {}
```

Nginx will need this configuration to listen for any connection at port 80 and serve the contents of `/usr/share/nginx/html`. This configuration may change based on your requirements.

**Update the Dockerfile for Nginx**
For now, our Dockerfile is only going to build a React app image but we can also create another image for Nginx which will save us from manual setup of Nginx on any target machine and configure it for our app.
So open up the Dockerfile again and add these steps at the bottom of the file.

```
FROM nginx:latest as prod

COPY --from=build /app/build /usr/share/nginx/html
COPY nginx.conf /etc/nginx/nginx.conf

EXPOSE 80/tcp

CMD ["/usr/sbin/nginx", "-g", "daemon off;"]
```
`FROM nginx:latest as prod` gets the latest Nginx docker image and annotates it as `prod` for production usage.

`COPY --from=build /app/build /usr/share/nginx/html` from the build stage of the image copy the build directory contents from `/app/build` to `/usr/share/nginx/html` which is the default directory for Nginx to server html contents but it can be changed if needed.

`COPY nginx.conf /etc/nginx/nginx.conf` This will copy the `nginx.conf` file created earlier and replace the contents of the `/etc/nginx/nginx.conf` in the target machine.

`EXPOSE 80/tcp` exposes port 80 and listens for incoming connections and serves the React app.

`CMD ["/usr/sbin/nginx", "-g", "daemon off;"]` fire up the nginx server and keep it alive.

> NOTE: These types of images where we have two different stages of builds (i.e. React app and Nginx) are called Multi-stage builds

**Finally build a docker image and run the docker container**

To build the docker image use this command

```
docker build -t with-docker:1.0.0-prod .
```
After the image build is complete run the container using this image with this command

```
docker run -d -p 80:80 --name react-server with-docker
```
Now your Nginx server is listening at port 80 and will serve your React app.

**Thank you for reading. Peace out.**