Building a Production-Ready Docker Image for a React Vite Application Using Multi-Stage Builds

Mahendhiran M
5 min readFeb 23, 2025

--

When deploying a React Vite application to production, it’s essential to ensure that the Docker image is optimized for size, security, and performance. One of the best ways to achieve this is by using multi-stage builds in Docker. This approach allows you to separate the build process from the final production image, resulting in a smaller, more secure, and efficient container.

In this blog post, I’ll walk you through creating a production-ready Dockerfile for a React Vite application using multi-stage builds. By the end, you’ll have a lightweight Docker image that serves your application using Nginx.

Why Use Multi-Stage Builds?

Multi-stage builds are a powerful feature in Docker that allow you to use multiple FROM statements in a single Dockerfile. Each stage can use a different base image, and only the final stage is included in the output image. This approach offers several benefits:

  1. Smaller Image Size: By excluding build tools and dependencies from the final image, you significantly reduce its size.
  2. Improved Security: Fewer components in the final image mean fewer potential vulnerabilities.
  3. Efficient Caching: Docker caches each stage, so if only the application code changes, the build stage can be reused, speeding up the build process.

Step-by-Step Guide to Building the Dockerfile

Let’s dive into creating a Dockerfile for a React Vite application using multi-stage builds.

Stage 1: Create react application using vite

To create a React app using Vite, follow these steps

Dockerfile and nginx.conf for build container

Stage 2: Build the Application

In the first stage, we’ll use a Node.js image to install dependencies and build the application.

# Stage 1: Build the application
FROM node:18-alpine AS build
# Set the working directory
WORKDIR /app
# Copy package.json and package-lock.json
COPY package.json package-lock.json ./
# Install dependencies
RUN npm install
# Copy the rest of the application code
COPY . .
# Build the application
RUN npm run build

What’s Happening Here?

  • Base Image: We use node:18-alpine because it’s lightweight and includes Node.js, which is required to build the React Vite application.
  • Dependencies: We copy package.json and package-lock.json and run npm install to install the dependencies.
  • Build: The npm run build command generates the production-ready files, which are typically placed in the dist directory.

Stage 2: Serve the Application Using Nginx

In the second stage, we’ll use an Nginx image to serve the built application.

# Stage 2: Serve the application using Nginx
FROM nginx:alpine AS production
# Copy the build output from the previous stage
COPY --from=build /app/dist /usr/share/nginx/html
# Copy the custom Nginx configuration
COPY nginx.conf /etc/nginx/conf.d/default.conf
# Expose port 80
EXPOSE 80
# Start Nginx
CMD ["nginx", "-g", "daemon off;"]

What’s Happening Here?

  • Base Image: We use nginx:alpine because it’s lightweight and optimized for serving static files.
  • Copy Build Output: The dist folder from the build stage is copied to Nginx’s default HTML directory (/usr/share/nginx/html).
  • Nginx Configuration: A custom nginx.conf file is copied to configure how Nginx serves the application.
  • Expose Port 80: The container listens on port 80, the default HTTP port.
  • Start Nginx: The CMD instruction starts Nginx in the foreground.
# Stage 1: Build the application
FROM node:18-alpine AS build

# Set the working directory
WORKDIR /app

# Copy package.json and package-lock.json
COPY package.json package-lock.json ./

# Install dependencies
RUN npm install

# Copy the rest of the application code
COPY . .

# Build the application
RUN npm run build

# Stage 2: Serve the application using Nginx
FROM nginx:alpine AS production

# Copy the build output from the previous stage
COPY --from=build /app/dist /usr/share/nginx/html

# Copy the custom Nginx configuration
COPY nginx.conf /etc/nginx/conf.d/default.conf

# Expose port 80
EXPOSE 80

# Start Nginx
CMD ["nginx", "-g", "daemon off;"]

Example nginx.conf

Here’s an example of a simple Nginx configuration file:

server {
listen 80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html;
try_files $uri $uri/ /index.html;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}

This configuration ensures that:

  • The application is served from the dist folder.
  • All routes are redirected to index.html, which is necessary for single-page applications (SPAs) like React.
  • Custom error pages are provided for 5xx errors.

Building and Running the Docker Image

Once the Dockerfile and nginx.conf are ready, you can build and run the Docker image.

1. Build the Docker Image

Run the following command to build the Docker image:

docker build -t my-react-vite-app .

2. Run the Docker Container

Start a container from the image:

docker run -p 8080:80 my-react-vite-app

3. Access the Application

Open your browser and navigate to http://localhost:8080. You should see your React Vite application running!

Benefits of This Setup

  1. Optimized Image Size: The final image only includes the built files and Nginx, resulting in a smaller size compared to including Node.js and build tools.
  2. Production-Ready: Nginx is a robust and efficient web server, making it ideal for serving static files in production.
  3. Security: By excluding build tools and development dependencies, you reduce the attack surface of the container.

Final Thoughts

Using multi-stage builds in Docker is a best practice for building production-ready images. It ensures that your final image is lightweight, secure, and optimized for performance. By following this guide, you can confidently deploy your React Vite application to any environment with minimal overhead.

Give it a try in your next project, and let me know how it works for you! If you have any questions or suggestions, feel free to leave a comment below.

Happy coding! 🚀

--

--

Mahendhiran M
Mahendhiran M

Written by Mahendhiran M

0 Followers

“Testing leads to failure, and failure leads to understanding.”

No responses yet