DEV Community

Cover image for πŸš€ Dockerize & Deploy a MERN Application (MongoDB, Express.js, React, Node.js)
Smooth Code
Smooth Code

Posted on

πŸš€ Dockerize & Deploy a MERN Application (MongoDB, Express.js, React, Node.js)

Containerizing a MERN application helps streamline development, ensure consistent environments, and enable smooth production deployments. In this guide, we’ll walk through Dockerizing both the Node.js backend and React/Next.js frontend, orchestrating services with Docker Compose, and finally configuring an Nginx reverse proxy for production.


🧱 Why Use Multi-Stage Docker Builds?

Traditional Docker builds often produce large images containing dependencies and files needed only during development. Multi-stage builds solve this by:

  • Separating build dependencies from the runtime environment
  • Significantly reducing the final image size
  • Improving security by excluding tooling from production

Multi-Stage Docker


πŸ”§ Environment Variables Overview

Backend (Node.js)

Environment variables determine how the Express server connects to MongoDB:

const MONGO_USER = process.env.MONGO_USER || "admin";
const MONGO_PASS = process.env.MONGO_PASS || "admin";
const MONGO_HOST = process.env.MONGO_HOST || "localhost";
const MONGO_PORT = process.env.MONGO_PORT || "27017";
const MONGO_DB   = process.env.MONGO_DB   || "notesdb";
const PORT = process.env.PORT || 5000;
Enter fullscreen mode Exit fullscreen mode

Frontend (Next.js / React)

These are embedded during the build:

const API_HOST = process.env.NEXT_PUBLIC_BACKEND_HOST || "http://localhost:5000";
const BG_COLOR = process.env.NEXT_PUBLIC_BG_COLOR || "#f5f5f5";
Enter fullscreen mode Exit fullscreen mode

Note: Variables prefixed with NEXT_PUBLIC_ are compiled into the build.
Changing them later requires rebuilding the frontend unless runtime config is implemented.


πŸ—οΈ Dockerizing the Node.js Backend

# Base image for dependencies
FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production

# Final minimal image
FROM node:20-alpine AS final
WORKDIR /app
RUN addgroup -S app && adduser -S app -G app
COPY --from=build /app/node_modules ./node_modules
COPY . .
USER app
EXPOSE 5000
CMD ["node", "server.js"]
Enter fullscreen mode Exit fullscreen mode

🎨 Dockerizing the Next.js Frontend

# Build stage
FROM node:18-bookworm-slim AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
ARG NEXT_PUBLIC_BG_COLOR="red"
RUN NEXT_PUBLIC_BG_COLOR=${NEXT_PUBLIC_BG_COLOR} npm run build

# Production stage
FROM node:18-bookworm-slim AS final
WORKDIR /app
COPY --from=builder /app/.next ./.next
COPY --from=builder /app/public ./public
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./
CMD ["npm", "run", "start"]
Enter fullscreen mode Exit fullscreen mode

nextjs build stage

This Dockerfile builds a Next.js app in a multi-stage process, optimizing the final image by copying only necessary files. It sets a customizable background color via an argument during build.

For production, define backend host at build time:

ARG NEXT_PUBLIC_BACKEND_HOST="http://your-server-ip"
RUN NEXT_PUBLIC_BACKEND_HOST=${NEXT_PUBLIC_BACKEND_HOST} npm run build
Enter fullscreen mode Exit fullscreen mode

🧩 Orchestrating Everything with Docker Compose

Mern Docker-Compose

services:
  mongodb:
    image: mongo:7
    container_name: mongodb
    restart: unless-stopped
    environment:
      MONGO_INITDB_ROOT_USERNAME: admin
      MONGO_INITDB_ROOT_PASSWORD: admin
      MONGO_INITDB_DATABASE: notesdb
    volumes:
      - mongo-data:/data/db
    ports:
      - "27017:27017"
    networks:
      - mern-net

  nodejs:
    build:
      context: ./backend
    image: smoothcoode/mernbackend #change this to you username/imagename
    container_name: nodeapp
    restart: unless-stopped
    ports:
      - "5000:5000"
    environment:
      - MONGO_HOST=mongodb
    depends_on:
      - mongodb
    networks:
      - mern-net

  frontend:
    build:
      context: ./frontend
    image: smoothcoode/mernfrontend #change this to you username/imagename
    ports:
      - "3000:3000"
    restart: unless-stopped
    depends_on:
      - nodejs
    networks:
      - mern-net

volumes:
  mongo-data:

networks:
  mern-net:
    driver: bridge
Enter fullscreen mode Exit fullscreen mode

🌐 Setting Up Nginx Reverse Proxy (Production)

server {
    listen 80;
    server_name _;

    # Frontend
    location / {
        proxy_pass http://localhost:3000;
        proxy_set_header Host $host;
        proxy_redirect http://localhost:3000/ http://$host/;
    }

    # Backend API
    location /api/ {
        proxy_pass http://localhost:5000;
        proxy_set_header Host $host;
    }
}
Enter fullscreen mode Exit fullscreen mode

βœ… Conclusion

By using multi-stage Docker builds and Docker Compose, we create a lightweight, secure, and scalable deployment workflow for MERN applications. Adding Nginx allows us to cleanly route frontend web traffic and API requests in production.

You now have:

  • Dockerized Node.js and Next.js applications
  • A persistent MongoDB container
  • A unified environment via Docker Compose
  • A production-ready reverse proxy setup

Top comments (0)