Deploying MERN Monorepo with Docker & Render

Share with a friend:

In this article, you will learn how to deploy a MERN (MongoDB, Express, React, Node.js) monorepo using Docker and deploy it on Render for free.

Prerequisites

  • Signup and install Docker
  • Create Dockerfile and Docker image for project
  • Signup for Render

Example Project Structure

Your project may likely be structured like the following:

/client
  --> src/
  --> package.json
/server
  --> /public (build files generated by React client)
  --> src/
  --> .env
  --> package.json
Dockerfile
.dockerignore
package.json (root package.json file containing script for deploy)

The client folder would contain a React project created with create-react-app (CRA) or Vite. In that project, you should have a package.json file containing a build script.

  "scripts": {
    "start": "react-scripts start",
    "build": "BUILD_PATH=../server/public react-scripts build",
    ...
  },

As you can see the build path is set to the /server/public directory in order to serve the static files using Express.js which also handles any API requests.

In your package.json file at the root of your project, you may have the following scripts to do project setup and deployment:

"scripts": {
    "install-server": "npm install --prefix server",
    "install-client": "npm install --prefix client",
    "install": "npm run install-server && npm run install-client",
    "deploy": "npm run build --prefix client && npm start --prefix server",
    ...
  }

Moving to the server, you may have configuration for connecting to a MongoDB database, signing JWT tokens, sending emails, logging, etc. These services may require sensitive information that you would store using environment variables typically in a .env file. Here is an example:

NODE_ENV=development
DB_CONNECTION_STRING=<connection-string-here>
JWT_SECRET=<jwt-secret-here>
JWT_EXPIRES_IN=<value-here>

Your project would not be able to run without the values in this file being passed correctly and the best way to do that is to use a secret file in your Docker build. You will see how you can accomplish that using Render.

Introduction to Render

Render is a cloud application platform that makes it easy for developers to build, deploy, and scale their applications. It is designed to provide a seamless and efficient experience for deploying and managing web services, static sites, background workers, and more. Render is a great tool and has key features such as automatic Git deploys, zero-downtime deployments, and more. Also, it has a generous free tier which can be used to deploy a small to medium-sized MERN monorepo project.

Creating and Deploying Web Service with Render

After signing up, on your Render dashboard, select the ‘New’ button then select ‘Web Service’.

After authorizing Render to access your GitHub repositories, your repositories will be listed on Render. From this list, you can easily select the repository containing your project.

When you select your project, Render should automatically detect that your project uses Docker. If it does not, you can manually specify this by selecting Docker from the ‘Language’ dropdown. Render will also automatically pre-fill other settings for you. You can review and modify them as you see fit.

Dealing with Secrets

Render allows you to specify environment variables or to use a secret file that can be accessed during builds and at runtime from your app’s root or from path /etc/secrets/<secret-filename>.

Since you are utilizing Docker, using a secret file works great. Learn more about using secret files in Docker builds.

Select ‘Add Secret File’, you can go with the standard .env as the filename and then paste the contents of your .env file.

Dockerfile

Now, you can build your image with a secret mount for that file by adding the following to the top of your Dockerfile:

# syntax = docker/dockerfile:1.2

Then, the following RUN instruction (considering that you named your secret file .env)

RUN --mount=type=secret,id=_env,dst=/etc/secrets/.env cat /etc/secrets/.env

Here is an example of a full Dockerfile for a MERN monorepo with the adjustments specified above:

# syntax = docker/dockerfile:1.2

FROM node:lts-alpine

WORKDIR /app

COPY package*.json ./

COPY client/package*.json client/
RUN npm run install-client --omit=dev

COPY server/package*.json server/
RUN npm run install-server --omit=dev

COPY client/ client/
RUN npm run build --prefix client

COPY server/ server/

RUN --mount=type=secret,id=_env,dst=/etc/secrets/.env cat /etc/secrets/.env

USER node

CMD [ "npm", "start", "--prefix", "server" ]

EXPOSE 3001

That’s it! Click ‘Deploy Web Service’ and Render will take care of the rest. You can visit the build logs to resolve any errors you might get. First, ensure you are able to build your project locally without any errors.

Render will provide you with a URL to visit your live site. Also, it has the functionality to use a custom domain.

Resolving MongoDB Network Access Exception

Your new Render site will fail to connect to your MongoDB database if you do not whitelist its IP address to allow network access. To resolve this, visit your MongoDB Atlas cloud dashboard, from the side navigation menu, go to ‘Security’ then ‘Network Access.’ Add the IP addresses of your Render web service, which you can find by clicking your project from the main Render dashboard, which will then navigate to a page giving you all the settings and information on the project. Specifically, select the ‘Connect’ button and you will see your list of IPs.

Share with a friend:

Rajae Robinson

Rajae Robinson is a young Software Developer with over 3 years of work experience building websites and mobile apps. He has extensive experience with React.js and Next.js.

Recent Posts