DEV Community

Uwadone Joshua
Uwadone Joshua

Posted on

Step-by-step guide to containerize your full-stack MERN application, using Docker Compose.

Here’s a step-by-step guide to containerize your full-stack MERN application, building each stack component locally first and then moving to Docker Compose.

Step 1: Clone Your GitHub Repository and Navigate to Local Directories

  1. Clone your GitHub repository: Copy your code from your github repository as shown below

github

Go to your terminal and run:
bash
git clone https://github.com/Uwadon1/dockerize-a-mern-stack-app.git
``

gitclone

Next you’ll have to move into the newly cloned directory

cd dockerize-a-mern-stack-app

  1. Directory structure check: Ensure your project folders has the Frontend/Web-tier, Backend/app-tier and Database. If it has, our final set would most likely look like this, please note because we are deploying our database directly from the backend: ` project-directory/ ├── frontend/ ├── backend/ ├── database/ └── docker-compose.yml (this will be created later)

Best Practices: Consider using a .dockerignore file to exclude unnecessary files from the image

Step 2: Create Dockerfiles for Each Stack Component

  1. Frontend (React)
    • Navigate to the frontend folder and create a Dockerfile:

`dockerfile
# Dockerfile for frontend

Use the official image as a parent image
Description: Dockerfile for the client side of the MERN stack application

Use the official image as a parent image

FROM node:18.9.1

Set the working directory

WORKDIR /app

Copy the file from your host to your current location

COPY package.json .

Run the command inside your image filesystem

RUN npm install

Inform Docker that the container is listening on the specified port at runtime

EXPOSE 5173

Copy the rest of your app's source code from your host to your image filesystem

COPY . .

Run the specified command within the container

CMD ["npm", "run", "dev"]

Frontend Dockerfile

  • Explanation: This Dockerfile installs dependencies, builds the frontend code, and uses serve to host the static files. Dockerfile Breakdown This Dockerfile builds a container image for a React frontend application, leveraging the Node.js environment. Let's break down each line:
    1. FROM node:18.9.1: This line specifies the base image for the container. node:18.9.1 indicates that we're using the official Node.js 18.9.1 image as the starting point.
    2. WORKDIR /app: This sets the working directory within the container to /app. This is where all subsequent commands will be executed.
    3. COPY package.json .: This copies the package.json file from the host machine to the /app directory within the container. This file contains the project's dependencies and configuration.
    4. RUN npm install: Executes the npm install command within the container to install all the dependencies listed in the package.json file.
    5. EXPOSE 5173: Informs Docker that the container will listen on port 5173. This port is often used by development servers like Create React App.
    6. COPY . .: Copies all the remaining files and directories from the host machine to the /app directory within the container. This includes the source code for your React application.
    7. CMD ["npm", "run", "dev"]: Specifies the default command to be executed when the container starts. In this case, it runs the dev script defined in the package.json file. This script typically starts the development server for your React app.

Building and Running the Image: To build the Docker image, you would use the following command:
Bash
docker build -t mern-frontend .

This command will create an image named mern-frontend based on the Dockerfile in the current directory. NB: Please ensure your docker desktop is running before you build your docker image.
As shown below, you can see that our front end image has been built successfully

Frontend Image Build

Frontend Dockerdesktop Image Build

Before we run, we need to create a network so that our containers can communicate among themselves seamlessly. To create our docker network, we would use:
Bash
docker network create mern

Next, we will have to run our container, To run our container, we would use:
Bash
docker run --name=frontend --network=mern -d -p 5173:5173 mern-frontend

This command will start a container based on the mern-frontend image, mapping port 5173 of the container to port 5173 of your host machine. This allows you to access your React app in the browser at http://localhost:5173.

Frontend App

  1. Backend (Node.js & Express)

It is best practice to always start a database container first, to avoid error.
Database (MongoDB)

  • For MongoDB, you don’t need a Dockerfile, as MongoDB has an official image available on Docker Hub.

To run our database container, we would use:
Bash
docker run --name=mongodb --network=mern -d -p 27017:27017 -v ~/opt/data:data/db mongo:latest

  • Navigate to the backend folder and create a Dockerfile:

    `dockerfile
    # Dockerfile for backend

FROM node:18.9.1

WORKDIR /app

COPY package.json .

RUN npm install

COPY . .

EXPOSE 5050

CMD ["npm", "start"]

Backend Dockerfile

  • Explanation: This sets up the backend service to listen on port 5000 and use npm start to initiate. Dockerfile Breakdown for the Backend This Dockerfile is designed to build a container image for a Node.js backend application. Let's break down each line:
    1. FROM node:18.9.1: This line specifies the base image for the container. node:18.9.1 indicates that we're using the official Node.js 18.9.1 image as the starting point.
    2. WORKDIR /app: Sets the working directory within the container to /app. This is where all subsequent commands will be executed.
    3. COPY package.json .: Copies the package.json file from the host machine to the /app directory within the container. This file contains the project's dependencies and configuration.
    4. RUN npm install: Executes the npm install command within the container to install all the dependencies listed in the package.json file.
    5. COPY . .: Copies all the remaining files and directories from the host machine to the /app directory within the container. This includes the source code for your Node.js backend application.
    6. EXPOSE 5050: Informs Docker that the container will listen on port 5050. This is the port that your Node.js server will be listening on.
    7. CMD ["npm", "start"]: Specifies the default command to be executed when the container starts. In this case, it runs the start script defined in the package.json file. This script typically starts your Node.js server. Building and Running the Image: To build the Docker image, you would use the following command: Bash docker build mern-backend .

This command will create an image named mern-backend based on the Dockerfile in the current directory.

Backend Image Build

Backend Image DockerDesktop Build

To run the container, you would use:
Bash
docker run --name=backend --network=mern -d -p 5050:5050 mern-backend

This command will start a container based on the mern-backend image, mapping port 5050 of the container to port 5050 of your host machine. This allows you to access your Node.js backend application at http://localhost:5050.

Image description

Haven built our containers and deployed our MERN stack locally, which really isn’t best practice, I’ll like to show how you can run all the commands in one single file and with just one command.:
Create a docker-compose.yml File

In your project root directory, create a docker-compose.yml to orchestrate all services together.

`yaml
services:
frontend:
build: ./mern/frontend
ports:
- "5173:5173"

networks:
- mern_network
environment:
REACT_APP_API_URL: http://backend:5050

backend:
build: ./mern/backend
ports:
- "5050:5050"
networks:
- mern_network
environment:
MONGO_URI: mongodb://mongo:27017/mydatabase

depends_on:
- mongodb

mongodb:
image: mongo:latest

ports:
- "27017:27017"

networks:
- mern_network
volumes:
- mongo-data:/data/db

networks:
mern_network:
driver: bridge

volumes:
mongo-data:
driver: local # Persist MongoDB data locally

Docker Compose

Docker Compose2

Run Docker Compose

  1. In the root project directory, run: `bash docker-compose up -d `

Docker Compose up

Docker Compose up2

This setup should provide a fully functional MERN application running in Docker!

Let me know if you run into any issues or if you'd like more detail on specific parts of this process.

  1. Access your services:
    • Frontend: http://localhost:3000
    • Backend: http://localhost:5000
    • MongoDB is accessible to the backend container at database:27017.

Verify the Connection Between Services
Confirm that:

  • The frontend can send requests to the backend.
  • The backend connects to MongoDB using the MONGO_URI.

Manage and Stop Containers

  • To stop containers, press Ctrl + C.
  • To stop and remove containers, networks, and volumes created by docker-compose up, run: `bash docker-compose down `

Step 3: Cleaning Up Your Docker Environment (Optional)
When you're done working with the application, you can clean up your docker environments, using any of these commands:

`bash
docker image prune -f
`

This command removes all unused images.

`bash
docker container prune -f
`

This command removes all unused containers.

`bash
docker network prune -f
`

This command removes all unused network.

`bash
docker compose down –volumes
`

This command removes the associated volumes, freeing up disk space.

`bash
docker system prune -f
`

This command removes all unused resources at once.

Note: The -f flag forces the removal without prompting for confirmation. Use it with caution, as it will remove resources without confirmation.
By following these steps and using the appropriate commands, you can effectively clean up your Docker environment and keep it organized.

Additional Considerations

  • Environment Variables: In production, you should use Docker secrets or environment variable files (.env) to handle sensitive data like database credentials.
  • Scaling: You can scale services (like the backend) by modifying your docker-compose.yml: `yaml backend: ... deploy: replicas: 3 `
  • Multi-stage Builds: For more complex setups, consider using multi-stage builds in your Dockerfiles to optimize the size of your Docker images.

Conclusion
By following this guide, you now have a fully Dockerized three-tier web application, with the frontend, backend, and database running in isolated containers, all connected via Docker Compose. You can easily scale, share, and deploy your application using this setup.

Top comments (0)