DEV Community

Duc Le
Duc Le

Posted on

Reduce Docker Image size for your Next.js App

Introduction

First thing first, I expect you to know what is Docker, but if you don’t:

Docker is an open platform for developing, shipping, and running applications

You can spend time learning about it

NextJS, in other hand, is a flexible React framework that gives you building blocks to create fast web applications.

NextJS, in other hand, is a flexible React framework that gives you building blocks to create fast web applications.

Dockerize your App

Before we optimize anything, we have to dockerize the application first. Let’s say our application name is my-space . Starting with:

Create the Dockerfile :



touch Dockerfile


Enter fullscreen mode Exit fullscreen mode

Ignore unnecessary files in dockerignore:



node_modules
.next
.vscode
.gitignore
README.md
.dockerignore
.git


Enter fullscreen mode Exit fullscreen mode

Dockerize it:

Image description

This is the most basic example on how to dockerize your app, now let’s build it with:



docker build -t my-space .


Enter fullscreen mode Exit fullscreen mode

Now look at the size:

Image description

*That’s just crazy, 2.42gb!!
*

Unbelievable right, we can’t publish this image, it’s just too heavy !

Reduce the image size

Use alpine

The Node.js Docker team maintains a node:alpine image tag and variants of it to match specific versions of the Alpine Linux distributions with those of the Node.js runtime. The original version size is about 1gb.

Now we will move to the alpine version:

Image description

Image description

Now the size shrank to 1.65gb, 800mb smaller. That’s a good start !

Multi-stages builds

Multi-stage builds are useful to anyone who has struggled to optimize Dockerfiles while keeping them easy to read and maintain.

We will create 2 stages in the Dockerfile . I will call it builder and runner

In this way we can get rid of unnecessary files in our image:

Image description

We will pick files from the builder and move it to the runner that we will eventually use:

Image description

*The size comes down to 1.36gb, about 300mb smaller, we are doing good !
*

Remove duplicate layers

You can see something is duplicating. Yes, we install the dependencies twice for each stage. Although this works and the project size is still the same. The image size is still big because of caching and layers.

So we can pick the node_modules from the build stage:

Image description

Image description

The size now is quite decent for a NextJS app, below 500mb

But we can still make it lighter !

Output File Tracing

During a build, Next.js will automatically trace each page and its dependencies to determine all of the files that are needed for deploying a production version of your application.

This feature helps reduce the size of deployments drastically. Previously, when deploying with Docker you would need to have all files from your package’s dependencies installed to run next start. Starting with Next.js 12, you can leverage Output File Tracing in the .next/ directory to only include the necessary files.

In your next.config.js file, enable the standalone output



experimental: {
    outputStandalone: true,
  },


Enter fullscreen mode Exit fullscreen mode

This will create a folder at .next/standalone which can then be deployed on its own without installing node_modules.

Image description

Image description

*The size is now 176mb! Small enough for most cases
*

Conclusion

This is just a simple example on how to optimize your docker image sizes, you can look deeper in Docker docs to find the most suitable treatment for your app!

Top comments (5)

Collapse
 
webmastersmith profile image
Bryon Smith • Edited

Thank you Duc Le for the great tutorial!
Here's updated code in a dockerfile.

  1. npx create-next-app@latest project-name
  2. cd into project-name
  3. edit next.config.js
  4. create .dockerignore
  5. create dockerfile
  6. docker build -t next .
  7. docker run -it --rm -dp 3000:3000 --name coNext next
  8. docker exec -it coNext sh
  9. enjoy!

next.config.js

/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  output: 'standalone',
};

module.exports = nextConfig;
Enter fullscreen mode Exit fullscreen mode

.dockerignore

node_modules
.next
.vscode
.gitignore
README.md
.dockerignore
.git
package-lock.json
Enter fullscreen mode Exit fullscreen mode

dockerfile

## Build image and name it 'next'
# docker build -t next .

## Run container and name it 'coNext'. Webpage is localhost:3000
# docker run -it --rm -dp 3000:3000 --name coNext next

## Connect to container
# docker exec -it coNext sh

## Stop docker container
# docker stop coNext

## All together
# docker stop coNext & docker image rm -f next & docker build -t next . && docker run -it --rm -dp 3000:3000 --name coNext next && docker exec -it coNext sh

# Start Dockerfile
ARG VERSION=alpine3.17
ARG DIR=my-space

FROM node:${VERSION} as builder
# redeclare ARG because ARG not in build environment
ARG DIR 
WORKDIR /${DIR}
COPY . .
RUN npm i
RUN npm run build

FROM node:${VERSION} as runner
# redeclare ARG because ARG not in build environment
ARG DIR
WORKDIR /${DIR}
COPY --from=builder /${DIR}/public ./public
COPY --from=builder /${DIR}/.next/standalone .
COPY --from=builder /${DIR}/.next/static ./.next/static

EXPOSE 3000
ENTRYPOINT ["node", "server.js"]
Enter fullscreen mode Exit fullscreen mode
Collapse
 
milesbd profile image
milesbd

Love this! Did a similar change to reduce an app (older version of next) from ~1.25gb to ~325mb.

I added
RUN yarn --frozen-lockfile --production
After yarn build to reduce the size of node modules and exclude dev dependencies from the final image.

Collapse
 
martinratinaud profile image
Martin Ratinaud

Awesome article, thanks

Can you plese share a gist so that we can copy the code from?

Thanks

Collapse
 
tcelestino profile image
Tiago Celestino

Great article.

Regarding your question, can I apply the same concept to an API project? Another question: what software are you using to analyze the size of the Docker image?

Collapse
 
leduc1901 profile image
Duc Le

I use Docker itself