Over 2,000 mentors available, including leaders at Amazon, Airbnb, Netflix, and more. Check it out
Published

How to Dockerize a React App and Deploy It Easily

Learn how to dockerize a React application and deploy it effortlessly using Docker, Nginx, and Docker Compose. Improve your DevOps lifecycle now!
Victor Rubio

Cloud Engineer

Do you want to learn how to dockerize a React app and make it ready for deployment? In this article, I’ll show you how to do that with a practical example from my GitHub repo.

Prerequisites

Ensure you have a foundational understanding of React, Docker, and basic shell scripting. Ensure Docker and Node.js are installed on your machine, and you have a code editor ready for some hands-on action!

Understanding the React Application Structure

The React application in our repository is structured with various components, dependencies, and configurations. The app is a simple goal tracker that lets you add, edit, and delete your goals. It uses React Hooks, React Router, and Axios to create a dynamic and interactive UI. The app also communicates with a backend API that uses MongoDB as the database. The app’s structure is as follows: 

  • package.json: This file defines the dependencies and scripts for the app. We use libraries such as react, react-dom, react-router-dom, axios, and bootstrap.
  • src/index.js: This file renders the main App component and wraps it with a BrowserRouter component to enable routing.
  • src/App.js: This file defines the App component, which contains the main layout and logic of the app. It uses useState and useEffect hooks to manage the state and fetch data from the API. It also uses Switch and Route components to render different pages based on the URL path.
  • src/components: This directory contains the reusable components for the app, such as GoalList, GoalForm, GoalItem, Navbar, and Footer. Each component has its file with a .jsx extension.
  • src/pages: This directory contains the components for the different pages of the app, such as Home, About, and NotFound. Each page has its file with a .jsx extension.
  • src/styles: This directory contains the custom CSS styles for the app. We use Bootstrap as a base framework and override some styles in our files.

Crafting a Multi-Stage Dockerfile

The Dockerfile in the frontend directory employs a multi-stage build to create an optimized Docker image:

# frontend/Dockerfile
FROM node:16.3.0-alpine AS prod

WORKDIR /app

COPY package.json /app

RUN npm install

COPY . /app

RUN npm run build

FROM nginx:alpine

WORKDIR /usr/local/bin

COPY --from=prod /app/build /usr/share/nginx/html

COPY generate-config.sh .

COPY custom-nginx.template /etc/nginx/conf.d/

RUN chmod +x generate-config.sh

EXPOSE 80

ENTRYPOINT [ "/bin/sh", "generate-config.sh"]

  • Stage 1: Building the React Application 
  • Utilizes node:16.3.0-alpine for a lightweight build environment. Installs dependencies and builds the application using npm.
  • Utilizes node:16.3.0-alpine for a lightweight build environment.
  • Installs dependencies and builds the application using npm.
  • Stage 2: Setting Up the Nginx Server 
  • Adopts nginx:alpine to serve the built React application. Copies the build artifacts and custom Nginx configuration for serving the application.
  • Adopts nginx:alpine to serve the built React application.
  • Copies the build artifacts and custom Nginx configuration for serving the application.

Parameterizing Nginx Configuration

The custom Nginx configuration (custom-nginx.template) is designed to proxy API requests to a backend service and serve the React application for other routes. A shell script (generate-config.sh) dynamically generates the final Nginx configuration by substituting environment variables, providing flexibility to define the backend host at runtime.

# frontend/custom-nginx.template
upstream backend {
    server ${BACKEND_HOST};
}

server {
    listen 80;

    location /api/ {
      proxy_pass http://backend$request_uri;
    }

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

# frontend/generate-config.sh
#!/bin/sh
envsubst '$BACKEND_HOST' < /etc/nginx/conf.d/custom-nginx.template > /etc/nginx/conf.d/default.conf;
exec nginx -g "daemon off;";

Orchestrating Containers with Docker Compose

Docker Compose is a tool that lets you define and run multiple containers using a YAML file. It simplifies the process of building, running, and connecting containers.

The docker-compose.yaml file orchestrates the frontend and backend services, ensuring they can communicate and are deployed cohesively:

# docker-compose.yaml
version: '3.8'

services:
  mongodb:
    container_name: mongodb
    image: mongo:6.0
    volumes:
      - dbdata:/data/db
    networks:
      - goals-net
    env_file:
      - .env

  goals-backend:
    image: goals-backend:1.0.0
    container_name: goals-backend
    build:
      context: ./backend
      dockerfile: Dockerfile
    ports:
      - 8000:8000
    networks:
      - goals-net
    depends_on:
      - mongodb
    env_file:
      - .env

  goals-frontend:
    image: goals-frontend:1.0.0
    container_name: goals-frontend
    build:
      context: ./frontend
      dockerfile: Dockerfile
    networks:
      - goals-net
    ports:
      - 3000:80
    env_file:
      - .env
    depends_on:
      - goals-backend

networks:
  goals-net:
    driver: bridge

volumes:
  dbdata:

Local Development & Testing

To run the Dockerized React app locally, use docker-compose up to build and start the containers. Ensure the application communicates with the backend API effectively and debug using Docker logs and browser developer tools.

CI/CD Considerations for Dockerized React Apps

Integrating CI/CD pipelines, like GitHub Actions, can automate the build, test, and deployment of the Dockerized React app. Define workflows in .github/workflows to trigger on code pushes or pull requests, ensuring continuous delivery and integration.

Build Worfklow

This workflow triggers on every push or pull request to the main branch. It builds and tests both frontend and backend images using Docker commands. It also pushes the images to a Docker registry (such as Docker Hub or GitHub Packages) using secrets to store credentials. We have commented it out on our repo to avoid additional build runs. The code we've used is the following:

name: CI
on:
  pull_request:
    types:
      - review_requested
    branches:
      - main

jobs:
  backend:
    name: Backend
    runs-on: ubuntu-20.04
    steps:
      - name: Check out repository code
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v1
        with:
          node-version: '16.x'

      - name: Install and Test Backend
        run: |
          cd backend
          npm install
          npm test

  frontend:
    name: Frontend
    runs-on: ubuntu-20.04
    steps:
      - name: Check out repository code
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v1
        with:
          node-version: '16.x'

      - name: Install and Test Frontend
        run: |
          cd frontend
          npm install
          npm test

      - name: Build Frontend
        run: |
          cd frontend
          npm run build

Best Practices & Tips

  • Optimize Dockerfile: Leverage Docker layer caching by ordering instructions to install dependencies before copying all source files.
  • Manage Images: Regularly prune unused Docker images and containers to manage disk usage.
  • Security: Ensure only necessary ports are exposed and use trusted base images.

Conclusion

I hope you enjoyed this guide on how to dockerize a React app and make it deployable. You learned how to use Docker, Nginx, and Docker Compose to set up a uniform environment for your app in different phases of development, testing, and production. This will help you streamline your DevOps process and create better software.

If you liked this article, you can check my other blog posts and my mentorships plans at my MentorCruise Profile.

Thanks for reading!

References

Source code: GitHub repository  

Find an expert mentor

Get the career advice you need to succeed. Find a mentor who can help you with your career goals, on the leading mentorship marketplace.