Production Multistage Dockerfile For React Application 🚀
Tags: docker, devops, react, microservices
Published: 2024-03-01 20:29:38


## Introduction:

Welcome to the guide on creating a production-ready multistage Dockerfile for your React application, tailored for those new to multistage builds. In this journey, we'll explore how to optimize the build process, enhance security, and streamline the deployment of your React app using Docker's multistage capabilities.

---

## Understanding Multistage Builds:

Multistage builds are akin to crafting a refined recipe with distinct steps, contributing to the creation of an optimized final product. In Docker terms, this entails breaking down the process of constructing our app into smaller, more efficient stages. Let's walk through the steps in our Dockerfile.

---

## How to Set Up the Project

### Basic directory structure
After completing the following steps, our application directory structure will look like this:


```
docker-react/
├── docker-compose.yml
├── Dockerfile
├── package.json
├── package-lock.json
├── public/
│   ├── favicon.ico
│   ├── index.html
│   ├── logo192.png
│   ├── logo512.png
│   ├── manifest.json
│   └── robots.txt
├── README.md
└── src/
    ├── App.css
    ├── App.js
    ├── App.test.js
    ├── index.css
    ├── index.js
    ├── logo.svg
    ├── serviceWorker.js
    └── setupTests.js
```
This Docker image contains a simple React application created using create-react-app. The application displays a **"Hello, World!"** message and includes basic configurations for a progressive web app.

For your reference, You can checkout the GitHub repo for this project.

**GitHub Link:**  [https://github.com/panchanandevops/Building-Dockerfiles/tree/main/docker-react](https://github.com/panchanandevops/Building-Dockerfiles/tree/main/docker-react)

## Dockerfile for GoLang application

```Dockerfile
# Stage 1: Build Stage
# Use a specific version of the official Node.js image as the build stage
FROM node:21.5-bullseye AS build

# Set the working directory inside the container
WORKDIR /usr/src/app

# Copy package.json and package-lock.json to install dependencies efficiently and leverage layer caching
COPY package*.json ./

# Set up npm cache in a designated directory to improve caching
RUN --mount=type=cache,target=/usr/src/app/.npm \
    npm set cache /usr/src/app/.npm && \
    npm install

# Copy the entire application source code
COPY . .

# Run the build command to generate production-ready artifacts
RUN npm run build


# Stage 2: Deployable Image
# Use a specific version of the official Nginx image as the base image for the deployable image
FROM nginxinc/nginx-unprivileged:1.24-bullseye-perl

# Expose the port that the Nginx server will listen on
EXPOSE 8080

# Copy the built artifacts from the build stage to the Nginx HTML directory
COPY --from=build /usr/src/app/build /usr/share/nginx/html
```

---

Breaking down each stage helps us understand multistage Dockerfiles easily. When we look at each stage carefully, we can see why each instruction is there and what it does. This makes it clear why each line in the Dockerfile is important.

### Stage 1: Build Stage

```Dockerfile
# Use a specific version of the official Node.js image as the build stage
FROM node:21.5-bullseye AS build

# Set the working directory inside the container
WORKDIR /usr/src/app

# Copy package.json and package-lock.json to install dependencies efficiently and leverage layer caching
COPY package*.json ./

# Set up npm cache in a designated directory to improve caching
RUN --mount=type=cache,target=/usr/src/app/.npm \
  npm set cache /usr/src/app/.npm && \
  npm install

# Copy the entire application source code
COPY . .

# Run the build command to generate production-ready artifacts
RUN npm run build
```

#### Explanation:

- **Start with Node.js:** Utilize Node.js as the foundation for our build process, ensuring compatibility with a specific version (21.5-bullseye).

- **Organizational Excellence:** Establish a workspace (/usr/src/app) for maintaining order and cleanliness.

- **Copy Strategically:** Initially, copy only package.json and package-lock.json, akin to bringing in the recipe without unnecessary kitchen clutter.

- **Cache Optimization:** Set up an npm cache to expedite the process, analogous to organizing ingredients for swift access.

- **Building Excellence:** Execute the command (npm run build) to create the final product – our production-ready React app.

---

### Stage 2: Deployable Image

```Dockerfile
# Use a specific version of the official Nginx image as the base image for the deployable image
FROM nginxinc/nginx-unprivileged:1.24-bullseye-perl

# Expose the port that the Nginx server will listen on
EXPOSE 8080

# Copy the built artifacts from the build stage to the Nginx HTML directory
COPY --from=build /usr/src/app/build /usr/share/nginx/html
```

#### Explanation:

- **Nginx Takes Over:** Transition to Nginx (1.24-bullseye-perl) as our hosting platform, a lightweight server ideal for showcasing our React app.

- **Port Access:** Open port 8080, allowing users to access and interact with our app.

- **Artifact Transfer:** Move the final product (the build folder) from the Node.js workspace to the serving tray of Nginx. It's akin to presenting our finished creation in a beautifully arranged display.

---

## Building and Running Your Docker Image:

Build the Docker image:

```bash
docker build -t panchanandevops:v1.0.0 .
```

Run the container:

```bash
docker run -d --name my-react-container -p 8080:8080 panchanandevops:v1.0.0
```

Access your React application at [http://localhost:8080](http://localhost:8080) in your browser.

---

## Conclusion:

By following this refined process, you've successfully crafted and presented your React app using Docker. The multistage approach ensures organization, efficiency, and deployability. As you progress in your Docker journey, experiment with diverse recipes and enjoy serving your creations to the world. **Happy coding and containerizing!** 🌐🚢