DEV Community

Cover image for Deploying a React App with Docker using Nginx
Mofajjal Rasul
Mofajjal Rasul

Posted on • Edited on

1

Deploying a React App with Docker using Nginx

Deploying React applications in production demands efficient resource utilization, consistent environments, and optimized static asset delivery. This guide details a Docker-based strategy using multi-stage builds to separate compilation and runtime environments, ensuring minimal final image size and adherence to production best practices.


Dockerfile Architecture & Technical Workflow

# Build stage
FROM node:alpine AS builder
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm install --frozen-lockfile
COPY . .
RUN npm run build

# Run stage
FROM nginx:alpine
COPY --from=builder /app/build /usr/share/nginx/html

RUN echo 'server { listen 80; server_name _; root /usr/share/nginx/html; index index.html; location / { try_files $uri $uri/ /index.html; } }' > /etc/nginx/conf.d/default.conf

EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
Enter fullscreen mode Exit fullscreen mode

Stage 1: Build Phase (Node.js Compilation)

  1. Base Image:

    • node:alpine provides a minimal Node.js runtime with Alpine Linux (musl libc, ~5MB size), reducing attack surface and build time.
  2. Dependency Resolution:

    • COPY package.json package-lock.json ./ isolates dependency files to leverage Docker layer caching.
    • npm install --frozen-lockfile enforces strict version resolution from package-lock.json, ensuring deterministic builds.
  3. Source Compilation:

    • COPY . . transfers the entire React source code into the container.
    • npm run build triggers the React build script, generating optimized static assets (HTML, JS, CSS) in the /app/build directory.

Stage 2: Runtime Phase (Nginx Server)

  1. Base Image:

    • nginx:alpine offers a lightweight Nginx server (~23MB) with Alpine Linux, optimized for serving static content.
  2. Artifact Deployment:

    • COPY --from=builder /app/build /usr/share/nginx/html transfers compiled assets from the builder stage to Nginx’s default web root.
  3. Nginx Configuration:

    • The RUN echo 'server { ... }' > /etc/nginx/conf.d/default.conf command defines a custom server block:
      • listen 80: Binds to port 80 for HTTP traffic.
      • root /usr/share/nginx/html: Specifies the static asset directory.
      • try_files $uri $uri/ /index.html: Enables client-side routing for SPAs by falling back to index.html on 404 errors.
  4. Container Orchestration:

    • EXPOSE 80: Declares the container’s listening port.
    • CMD ["nginx", "-g", "daemon off;"]: Starts Nginx in foreground mode, a requirement for Docker containers.

Technical Rationale for Nginx

  1. Static Asset Delivery:

    • Nginx uses an event-driven, non-blocking architecture, efficiently serving static files with low memory overhead.
    • Built-in gzip compression and caching headers enhance performance for JS/CSS assets.
  2. Routing Handling:

    • The try_files directive ensures React Router or similar libraries manage client-side routing without server-side configuration.
  3. Security:

    • Alpine Linux’s minimal footprint reduces vulnerability exposure.
    • Nginx provides robust security headers and rate-limiting capabilities.

Build & Deployment Commands

  1. Image Creation:
   docker build -t react-app .
Enter fullscreen mode Exit fullscreen mode
  • -t react-app: Tags the image for identification.
  • .: Builds using the Dockerfile in the current context.
  1. Container Execution:
   docker run -p 80:80 react-app
Enter fullscreen mode Exit fullscreen mode
  • -p 80:80: Binds the host’s port 80 to the container’s port 80.

Key Benefits of Multi-Stage Builds

  1. Size Reduction:

    • Final image excludes Node.js, npm, and build dependencies, reducing size by ~90% compared to single-stage builds.
  2. Security Isolation:

    • Build tools (e.g., compilers, npm) are absent in the runtime image, minimizing exploit risks.
  3. Deterministic Builds:

    • --frozen-lockfile ensures dependency versions remain consistent across environments.

Conclusion

This implementation leverages Docker’s multi-stage build system to isolate development and production environments, combining Node.js’s build capabilities with Nginx’s optimized static asset delivery. The result is a secure, lightweight, and performant container ideal for scalable React deployments in cloud-native ecosystems.

Heroku

Amplify your impact where it matters most — building exceptional apps.

Leave the infrastructure headaches to us, while you focus on pushing boundaries, realizing your vision, and making a lasting impression on your users.

Get Started

Top comments (0)

AWS Q Developer image

Your AI Code Assistant

Automate your code reviews. Catch bugs before your coworkers. Fix security issues in your code. Built to handle large projects, Amazon Q Developer works alongside you from idea to production code.

Get started free in your IDE

👋 Kindness is contagious

Engage with a wealth of insights in this thoughtful article, valued within the supportive DEV Community. Coders of every background are welcome to join in and add to our collective wisdom.

A sincere "thank you" often brightens someone’s day. Share your gratitude in the comments below!

On DEV, the act of sharing knowledge eases our journey and fortifies our community ties. Found value in this? A quick thank you to the author can make a significant impact.

Okay