Dockerize a react app with NGINX for multiple environments
Tags: docker, react, nginx, environment
Published: 2020-02-06 19:02:05

Let's say you have your shiny web app and you want to containerize it and publish it.

In my case, it is a simple app based on create-react-app, which connects to a GraphQL backend and I use NGINX as a webserver to serve my built frontend. I have two environments, one of them is public (production) and the second one is secured with basic auth (staging). The two environments have different NGINX config files.

So let's get started step by step.

I have the React app and I created two Nginx config files
```
nginx.staging.conf
nginx.production.conf
```
Both NGINX configurations are based on Sara Vieiras article https://medium.com/yld-blog/deploy-your-create-react-app-with-docker-and-ngnix-653e94ffb537

So what I need to achieve is passing a build time environment variable into our React app build, and based on a second build time variable I need to choose the right Nginx configuration.

So first we define the base image then we copy our app into our work directory then we go to the root of our app. In my case it is a yarn workspaces monorepo and I have some business logic in the upper levels, that's why the long path.

```
FROM node as build
WORKDIR /app
COPY . .
WORKDIR /app/packages/reactapp
```

In my app I am using as my GrqphQL endpoint constant this value `process.env.REACT_APP_GRAPHQL_ENDPOINT || "http://localhost:4000/graphql"`.
So To build our app we need to pass a build time environment variable which is represented by `process.env.REACT_APP_GRAPHQL_ENDPOINT` otherwise it will use the localhost value.
To achieve this we initialize a build-time argument, and based on this argument we set our environment variable. It is very important that our value needs to start with `REACT_APP` otherwise create-react-app ignores our value.
( there are some exceptions read more: https://create-react-app.dev/docs/advanced-configuration )

```
ARG REACT_APP_GRAPHQL_ENDPOINT_ARG
ENV REACT_APP_GRAPHQL_ENDPOINT=$REACT_APP_GRAPHQL_ENDPOINT_ARG

RUN yarn
RUN yarn build
```
At this point, we have built our app with a custom build-time variable, now we can set up our Nginx web server, so let's continue our docker file. So we initialize an Nginx image and we copy our built React app into the folder which is set as root in our Nginx configuration.

```
FROM nginx:alpine
COPY --from=build /app/packages/reactapp/build /usr/share/nginx/html
```
Then we can continue with the initialization of our second environment variable which helps to decide which configuration file to pick from the two we created. I use the build-time argument to dynamically create the filename I want to use. And I also copy the file which contains the encrypted credentials for the basic auth for the staging environment.
```
ARG NGINX_ENV_CONF
COPY nginx.$NGINX_ENV_CONF.conf /etc/nginx/nginx.conf
COPY .htpasswd /etc/apache2/.htpasswd
```
In the end, we expose our port 80 and we fire up Nginx in the foreground.

```
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
```

so our `.dockerfile` looks like this:
```
FROM node as build
WORKDIR /app
COPY . .
WORKDIR /app/packages/reactapp

ARG REACT_APP_GRAPHQL_ENDPOINT_ARG
ENV REACT_APP_GRAPHQL_ENDPOINT=$REACT_APP_GRAPHQL_ENDPOINT_ARG

RUN yarn
RUN yarn build

FROM nginx:alpine
COPY --from=build /app/packages/reactapp/build /usr/share/nginx/html

ARG NGINX_ENV_CONF
COPY nginx.$NGINX_ENV_CONF.conf /etc/nginx/nginx.conf
COPY .htpasswd /etc/apache2/.htpasswd

EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
```

Now we can build our app for the staging:
```
docker build -t dockerized-react 
  --build-arg REACT_APP_GRAPHQL_ENDPOINT_ARG=http://mystaging.dev/graphql 
  --build-arg NGINX_ENV_CONF=staging .
```

Then we can run it like this:
```
docker run -d --name dockerized-react -p 80:80 dockerized-react
```

As an alternative to conditionally choosing a different Nginx configuration, we could use an Nginx template file, but in this case for me, it was easier to choose between files.

English isn’t my first language, so please excuse any mistakes.

Thank you all for your attention and time;