DEV Community

Cover image for Next.js + Docker. Made easy.
Kumar Abhirup
Kumar Abhirup

Posted on

Next.js + Docker. Made easy.

This week while starting to build a huge SaaS product, I had to make many decisions. The biggest decision I made was to build that SaaS product with the Microservices architecture.

Thankfully, Lucas Chen had this amazing series that explained the React + GraphQL + Docker Microservices architecture. In his series, the backends were microservices but React was not hosted on Docker. I wanted it all on Docker, so I had to research a lot, about integrating React (especially Next.js) with Docker.

After a few days of research and setting up a Containerized Next.js App, I am here to share with you how to do it.

Hope you like it :)


πŸ¦‹ Getting Started

Setting up a Next.js shouldn't be hard.

yarn create next-app
Enter fullscreen mode Exit fullscreen mode

Wait! We are not doing it all from scratch.

Instead, I would recommend you to clone this repo. We will learn about containerized Next.js from there. In this way, you will be able to compare your progress to that repository so that you can ensure you don't get lost in a long tutorial.

GitHub logo KumarAbhirup / dockerized

Boilerplate to start with Docker setup (Next.js included)

πŸ„ dockerized

emoji-log Twitter

πŸ“¦ Setup

πŸ–₯️ Development environment

  • Run
git clone https://github.com/KumarAbhirup/dockerized dockerized # to clone project
cd dockerized # enter in the project
docker-compose up
Enter fullscreen mode Exit fullscreen mode
  • Rename all the .env.example to .env.

  • Create a .env file in the root of the directory.

  • Visit http://localhost:3000/

βš’οΈ Linting

In VSCode

  • Install ESLint and Prettier VSCode extensions.
  • Done! Now you have live linting and autofixing setup!

In Any other IDE

  • Run yarn lint in indivisual packages to check for linting errors.
  • Run yarn lint:fix to fix the linting errors.

πŸ¦„ Info

  • We are following the micro-services architechture. That means, to install npm modules, you'll have to run yarn add in the respective packages.
  • To customize the linter, use .eslintrc and .prettierrc file. Learn more

πŸ“ License

MIT Β© Kumar Abhirup

Created by Kumar Abhirup πŸ‘‰ Twitter.

Peace ✌️

The above repository includes...

  • A setup that is scalable. You may append your dockerized backends to it later.
  • ESLint + Prettier setup included.
  • It's TypeScript. :)

πŸ”° Things you need

  • Docker Installed on your machine
  • Some basic knowledge of Next.js

πŸš€ Clone and Setup the repository

  • Run the below command
git clone https://github.com/KumarAbhirup/dockerized dockerized
cd dockerized
Enter fullscreen mode Exit fullscreen mode
  • Rename all the .env.example to .env. You'll find it in packages/landingpage

  • Create a .env file in the root of the directory.

As you cloned the project, the Next.js App is ready to run.

Just run the below command to fire up the development environment for the Next.js project.

docker-compose up
Enter fullscreen mode Exit fullscreen mode

πŸ‘ But Kumar, how does this thing even work?

You might be wondering where your Next.js project is staying.

It is in the packages/landingpage...

Next.js Project Folder Structure

You might be wondering why that Next.js project is kept deep inside the file system.

I did it because no one dockerizes Next.js when they are only using Next.js...

Dockerizing makes sense when you have a huge container architecture that connects your React frontends to the Containerized backends.

So, the repository would not just contain a Next.js project but would have backends kept in the same packages folder.


πŸ“¦ How to containerize Next.js?

To use Docker to containerize any code, we need to have a Dockerfile in the package. Every container has its own Dockerfile.

Next.js too, will have its own Dockerfile. Let us take a look at it.

packages/landingpage/Dockerfile

FROM node:12

ENV PORT 3000

# Create app directory
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app

# Installing dependencies
COPY package*.json /usr/src/app/
RUN npm install

# Copying source files
COPY . /usr/src/app

# Building app
RUN npm run build
EXPOSE 3000

# Running the app
CMD "npm" "run" "dev"
Enter fullscreen mode Exit fullscreen mode

Let me explain what's happening here. Here, by FROM node:12, we are telling Docker to work with the Node.js image.

ENV PORT 3000 just exposes the environment variable PORT=3000.

The below code snippet tells docker to create directories, namely /usr/src/app. We also tell Docker to use that directory as its primary workspace (for carrying out processes) hereafter.

RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
Enter fullscreen mode Exit fullscreen mode

The below code snippet copies package.json and package-lock.json from your local cloned repository to the Docker Container and then runs npm install on it. I recommend you to take a look at package.json of the Next.js container so you get the idea.

COPY package*.json /usr/src/app/
RUN npm install
Enter fullscreen mode Exit fullscreen mode

Now that we have all the node_modules ready, below code will copy our code from our local computer directory and will put it into the Docker Container Directory.

# Copying source files
COPY . /usr/src/app
Enter fullscreen mode Exit fullscreen mode

Then the Dockerfile builds the Next.js app, exposes port 3000 (where Next.js works by default), and runs the command npm run dev.

# Building app
RUN npm run build
EXPOSE 3000

# Running the app
CMD "npm" "run" "dev"
Enter fullscreen mode Exit fullscreen mode

I hope you understood all that is happening due to the Dockerfile.


Now, we have successfully containerized the application with Next.js! But we are not yet done.

For hot-reloading to work with Next.js and Docker, you need to have the below code snippet added to the packages/landingpage/next.config.js.

module.exports = {
  webpackDevMiddleware: config => {
    config.watchOptions = {
      poll: 1000,
      aggregateTimeout: 300,
    }

    return config
  },
}
Enter fullscreen mode Exit fullscreen mode

We are still not done!

To run all our containers (in this case only one) together successfully, we will need a docker-compose.yml file in the root of the project.

Check out the docker-compose.yml in your folder structure.

version: "3.3"

services:
  nextjs:
    ports:
      - 3000:3000
    build:
      context: packages/landingpage
      dockerfile: Dockerfile
    volumes:
      - ./packages/landingpage:/usr/src/app
      - /usr/src/app/node_modules
      - /usr/src/app/.next
    env_file:
      - .env
Enter fullscreen mode Exit fullscreen mode

The above code snippet makes sure that port 3000 is exposed. The docker-compose.yml file also tells Docker what services to build and which Dockerfile to use.

The env_file tells the composer to use a .env file which if you have not yet made in your project, please add it for it to work.

The volumes part is very important here. Without it, your Next.js will work, but the _Hot Reloading Developmental Feature` would not work.


πŸ”₯ Hurray!

If you surf through the repository carefully, you will understand how to containerize Next.js with Docker.

We are done!

To run the Dockerized Next.js app...

Run docker-compose up and open http://localhost:3000 in your browser.

To make changes in code, make changes to packages/landingpage/pages/index.tsx file to see your website development experience come alive.


πŸš€ For production

When deploying to production, just make sure that you make a small change in your packages/landingpage/Dockerfile.

Change the last line (CMD "npm" "run" "dev") to CMD "npm" "start".


❀️ Links


πŸ† About me

I am Kumar Abhirup, a 16-year-old JavaScript React developer from India who keeps learning a new thing every single day.

Connect with me on Twitter 🐦
My personal website and portfolio πŸ–₯️

Comment below your better ways, and suggestions to improve this post.Β :)

Top comments (10)

Collapse
 
daveteu profile image
Dave

Don't understand the part about not dockerizing nextJS alone.

Also cannot understand why you put nextJS in packages/landing page.

You spoke about backend, are you referring to your REST API? (which is what nextJS suppose to be), or are you speaking about your database servers, nginx etc (which in most case should be in a separate container)?

Good write up, but appreciate clarification if not it will be sending wrong information who came here while googling for this topic.

Collapse
 
hirokihokari profile image
hirokihokari

Is next build necessary if you're doing next dev?

Collapse
 
maxymapp profile image
Maksym Kulikovskiy • Edited

next build is only needed when you'll be serving your build with next start in Production environment.
On local dev environment, next dev is all you need.
In practice, what I've seen about Next.js is that running next dev after next build simply overwrites the existing build directory, so yeah next build is redundant.

The npm run dev, is optimized to run you local code with hot-reload. It is not meant for serving your dynamic site. When you use npm run build && npm run start, it creates a better-optimized output suitable for serving your site contents with code splitting and other good stuff. However, you have to understand how your data-fetching requests ( here authentication requests) work.
from github.com/vercel/next.js/discussi...

Collapse
 
codekagei profile image
themmyloluwaa

Hi @kumar_abhirup thank you for the nice writeup.

What's the size of your build ? I keep getting between 900Mb - 1Gb and my attempts to reduce the image size has proved futile. Any help would be greatly appreciated.

Collapse
 
katchvidal profile image
Katch • Edited

node:alpine
iΒ΄ve 650mb

Collapse
 
dicompathakofficial profile image
dicompathakofficial

Good Post Man. Btw recently started learning Next.js was looking for deployment options other than vercel. Thought of deploying it in digitalocean or linode. Your code seems great but propably is a bit too complex for a noob like me ><) Do you know any good resources for writing a docker script for a simple next js app. Thanks. And keep on doing the good job bro.. and keep on learning. Absolutely Proud of ya..

Collapse
 
rafipiccolo profile image
Raphael Piccolo

Thanks for the presentaion !
I have now deployed my nextjs containers in production.

But i strongly disagree on this : Dockerizing makes sense when you have a huge container architecture.
container are supposed to be light to start and stop fast. If you do a huge container, it will be counter productive when you grow.
it may make sens to have a big git for exemple with all your project inside (front, back, etc) to ease developpment, but you should deploy each part of the project on its own container using docker compose for exemple. that way scaling and monitoring is way more efficient.
have a good day.

Collapse
 
mylastore profile image
Oscar Quinteros

I was so hyped about this until I got to typescript. I still don't understand why you would use a useless library.

Collapse
 
daveteu profile image
Dave

what has this got to do with typescript.

Collapse
 
mylastore profile image
Oscar Quinteros

oops, put comment on wrong post I guess