Read Medium logo
No Results
Translate to
Read Medium Logo
Free OpenAI o1 chatTry OpenAI o1 API
Read Medium logo
No Results
Translate to
avatarItsuki

Summary

The web content provides a comprehensive guide on how to containerize a Next.js application using Docker, including both single-stage and multi-stage Dockerfile examples, and how to use Docker Compose for easier management, with troubleshooting tips for common issues encountered during the process.

Abstract

The article "Dockerize a Next.js App" outlines the process of deploying a Next.js application within a Docker container, which is essential for cloud platforms other than Vercel, such as AWS or Google Cloud Run. The author shares their personal experience with the dockerization process, emphasizing the lack of specific resources for Next.js compared to general Node.js applications. The guide includes a basic single-stage Dockerfile for development purposes and a more complex multi-stage Dockerfile for production, which optimizes build times and security by using a non-root user. Additionally, the article explains how to use Docker Compose to streamline the building and running of containers with a simple configuration. The author also addresses two specific problems they encountered, providing practical solutions to overcome these challenges.

Opinions

  • The author found that existing online resources for dockerizing Node.js applications were not directly applicable to Next.js, leading to time-consuming troubleshooting.
  • They suggest that adding the output: 'standalone' configuration to next.config.js is unnecessary, contrary to other solutions they found.
  • The author prefers using node:18-alpine for a smaller image size but notes the necessity of installing Python due to the Alpine version's minimalistic nature.
  • They advocate for the use of multi-stage Dockerfiles to improve build efficiency and security by adding a non-root user for running the application.
  • The author recommends using Docker Compose for its simplicity in managing container lifecycles with less command-line complexity.
  • They encountered a Python-related error and resolved it by either using a non-Alpine Node image or installing Python and other dependencies explicitly.
  • Another issue faced was related to the production build not being found, which was resolved by adjusting the volumes in the docker-compose.yml file rather than altering the Dockerfile with multiple CMD instructions.

Dockerize a Next.js App

If you want to deploy your Next.js App on Vercel, you will not need a container in this case. Since Next.js is created and maintained by Vercel, you can do your deployment with ease. However, if you are looking into running your App through AWS, Google Cloud Run or other cloud providers, you will need a container.

Most of the articles I found online explains how to dockerize a Node.js App, but not much focusing on a Next.js App. There are solutions out there, but those solutions gives me some errors that took me hours in figuring out. So I am just going to share the way I did it, the problems I encoutered, and my solutions to those.

Getting Started!

Start with any existing Next.Js project you have or simply clone one from one of the examples provided officially by Vercel. Some other solutions I found online suggested that configuring the application to build a standalone application through adding the following lines to next.config.js is a must, but I found everything worked fine without adding it.

const nextCofig = {
  output: 'standalone', // mine worked fine without this line
  // ... other config
}

DockerFile

Add a Dockerfile to your root repository with exactly this name. In this file, we will add instructions for the docker image we will be building.

I will share two versions of Dockerfile I created, one super simple and basic single stage one just for testing the developemnt stage/server, and another multi-stage one together with a docker-compose.yml that can be used both for production and development.

Single-stage DockerFile

Here is how the file look like and I will explain what I did line by line.

FROM node:18

WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD npm run dev

First of all, We tell the Docker to use the official Docker Node image version 18 through From node:18 , changing what comes after FROM to change the image to the one you want to use. You can find a list of supported ones here.

The WORKDIR sets the context for subsequent commands. You can name it whatever you like, I called mine app . We then copy the package.json and package-lock.json files to the container and RUN npm install will install all dependencies for us.

After that, we copy all the code from our project (current root directory) to our WORKDIR , which is /app in my case.

Expose 3000 tells the container that out app runs on the port 3000.

After all the setting finish, we have CMD npm run dev to ask the container to start the development server.

AND THAT’S IT for the Dockerfile!!

To build the docker Image from the Dockerfile we just created, simply run the following command. I name it my-app, but feel free to change it to what ever you want. Also don’t forget the . at last.

docker build -t my-app .

Once the image is created, we can run it by

docker run -p 3000:3000 my-app

3000:3000 specify the the port you want to run the app on, I will run mine on 3000. And if you access http://localhost:3000 , you will see your working app!

Multi-stage DockerFile

We will now be working on creating a multi-stage Dockerfile that can make our builds faster and more efficient, and allow us to switch between production and developement stage easily.

Delete whatever you have in your Dockerfile and replace it with the following.

FROM node:18-alpine as base
RUN apk add --no-cache g++ make py3-pip libc6-compat
WORKDIR /app
COPY package*.json ./
EXPOSE 3000

FROM base as builder
WORKDIR /app
COPY . .
RUN npm run build


FROM base as production
WORKDIR /app

ENV NODE_ENV=production
RUN npm ci

RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001
USER nextjs


COPY --from=builder --chown=nextjs:nodejs /app/.next ./.next
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./package.json
COPY --from=builder /app/public ./public

CMD npm start

FROM base as dev
ENV NODE_ENV=development
RUN npm install 
COPY . .
CMD npm run dev

Let’s use node:18-alpine in this case. It is a lot smaller than the main one. For -alpine to work , you will have to have python installed before everything else, and that is why we have that extra line of RUN apk add — no-cache g++ make py3-pip libc6-compat. We put our common settings in the base stage, so that we can reuse it in other stages later on. You can change the name of the stage by changing whatever comes after as .

The builder stage is basically just in charge of npm run build. This stage will be called when we try to COPY — from=builder in the production stage if those files did not already exist. See? Here is where a multi-stage Dockerfile comes in handy. The builder stage is only called when needed.

In our production stage, we set NODE_ENV to production and it is said that doing this will make the performance three times better. Next, we run npm ci, which is trargeting for continuous integration, instead of npm install .

We then add a non-root user to run the app for security reasons. I called by user nextjs and group nodejs because I am lazy.

After that, we copied the assets needed from builder stage by calling COPY — from=builder.

End finally, we start our start our application by calling npm start.

In our dev stage, we are basically doing the same thing as what we did in a single-stage Dockerfile so I am just going to skip it.

Before we moving onto creating a docker-compose.yml, if you want to check if your Dockerfile actually build, you can run docker build -t my-app . and docker run -p 3000:3000 my-app . Don’t forget to comment out the stage you don’t want to test on. For example, if you want to see if your production stage can be built and run successfully, simply comment out everything coming after FROM base as dev .

Docker Compose

With Docker Compose, we don’t need to remember all those long commands to build or run containers. We can simply just use docker-compose build and docker-compose up .

Add a docker-compose.yml file to your root directory with the following conent.

version: '3.8'
services:
  app:
    image: openai-demo-app
    build:
      context: ./
      target: dev
      dockerfile: Dockerfile
    volumes:
        - .:/app
        - /app/node_modules
        - /app/.next
    ports:
      - "3000:3000"

version : '3.8' sepcifies the version of Docker Compose we want to use. I only have one service in this case and that will be app, but you can add more to it based on your needs.

Build context specifies the current directory and target specifies the stage you want to build the Docker image with. If you want to run in production, simply set target:production.

Volume tells Docker to copy contents from the local directory ./ of the host to /app directory on the Docker container.

Finally, we map port 3000 on the host machine to port 3000 of the container. We exposed port 3000 when we built the container, and our app will also runs on 3000.

Testing with Docker Compose

We finally made our way to building the Docker image. We are going to use Docker’s BuildKit feature for a faster build.

COMPOSE_DOCKER_CLI_BUILD=1 DOCKER_BUILDKIT=1 docker-compose build

After the build finish, we can run the image and start the app by

docker-compose up

After that, if you go to http://localhost:3000 on your browser, you will see your app running!!!

That’s it!

Below are just some problems I encountered. I am going to share those with you really quick in case you are struggling on getting your docker container to work and trying to find out where might the problems be.

Problems I encountered

  1. Python is not set from command line or npm configuration: (As a mac user) I tried to add python to path, alias python to python3, uninstall and reinstall python, etc.etc.etc. NONE OF THOSE worked out for me. So here are the two solutions I found. (1) use node:18(or any other number you prefer) instead of node:18-alpine. (2) add RUN apk add — no-cache g++ make py3-pip libc6-compat before you do any of the installations of your packages.
  2. Could not find a production build in the ‘/app/.next’ directory when using docker-compose up: Everything worked as expected if I ran the image through docker run. There are solutions out there suggesting add CMD ["npm","run","build"] right before CMD npm start in the Dockerfile. However, this gave me an error saying no two CMD allowed. My solution was to add — /app/.next to volumes in Docker-compose.yml.

That’s all I have for today!

Thank you for reading! Hope those tips can help!

Nextjs
Docker
Docker Compose
Dockerfiles
Dockerize
Recommended from ReadMedium
avatarLet's Code Future
Dockerize a Next.js Application

how to Dockerize a Next.js Application

3 min read
avatarAsian Digital Hub
Next.js Docker Environment Variables Configuration

Next.js is a powerful React framework that helps developers build stunning web applications with ease. On the other hand, Docker offers a…

6 min read
avatarRajan Sharma
SSR, CSR, SSG, and Use-Client in Next.js

In Next.js, Server-Side Rendering (SSR) and Client-Side Rendering (CSR) are two rendering techniques that can be used to build React…

6 min read
avatarTari Ibaba
5 amazing new JavaScript features in ES15 (2024)

5 juicy ES15 features with new functionality for cleaner and shorter JavaScript code in 2024.

4 min read
avatarShuai Li
Can You Answer This Senior Level JavaScript Promise Interview Question?

Most interviewees failed on it.

3 min read
avatarRahul
Containerize Your Next.js 14 Application with Docker: A Step-by-Step Guide

Next.js, a leading React framework, empowers developers to build dynamic server-side rendered (SSR) and static web applications with ease…

6 min read