Optimize Dockerfile images for NextJS

ductnn

Duc Tran

Posted on August 5, 2022

Optimize Dockerfile images for NextJS

Optimize Dockerfile images for NextJS

NextJS Docker images is too big. So, this article will focus on the way optimize dockerfile for production. Here, i will use 2 ways for optimize docker images.

Full source code in here

Setup

First, we need to create a project with nextjs. Quickly, i use an example of Vercel, project in here.

Project structure:

.
├── @types
│   └── remark-html.d.ts
├── README.md
├── _posts
│   ├── dynamic-routing.md
│   ├── hello-world.md
│   └── preview.md
├── components
│   ├── alert.tsx
│   ├── avatar.tsx
│   ├── container.tsx
│   ├── cover-image.tsx
│   ├── date-formatter.tsx
│   ├── footer.tsx
│   ├── header.tsx
│   ├── hero-post.tsx
│   ├── intro.tsx
│   ├── layout.tsx
│   ├── markdown-styles.module.css
│   ├── meta.tsx
│   ├── more-stories.tsx
│   ├── post-body.tsx
│   ├── post-header.tsx
│   ├── post-preview.tsx
│   ├── post-title.tsx
│   └── section-separator.tsx
├── interfaces
│   ├── author.ts
│   └── post.ts
├── lib
│   ├── api.ts
│   ├── constants.ts
│   └── markdownToHtml.ts
├── next-env.d.ts
├── package.json
├── pages
│   ├── _app.tsx
│   ├── _document.tsx
│   ├── index.tsx
│   └── posts
│       └── [slug].tsx
├── postcss.config.js
├── public
│   ├── assets
│   │   └── blog
│   │       ├── authors
│   │       │   ├── jj.jpeg
│   │       │   ├── joe.jpeg
│   │       │   └── tim.jpeg
│   │       ├── dynamic-routing
│   │       │   └── cover.jpg
│   │       ├── hello-world
│   │       │   └── cover.jpg
│   │       └── preview
│   │           └── cover.jpg
│   └── favicon
│       ├── android-chrome-192x192.png
│       ├── android-chrome-512x512.png
│       ├── apple-touch-icon.png
│       ├── browserconfig.xml
│       ├── favicon-16x16.png
│       ├── favicon-32x32.png
│       ├── favicon.ico
│       ├── mstile-150x150.png
│       ├── safari-pinned-tab.svg
│       └── site.webmanifest
├── styles
│   └── index.css
├── next.config.js
├── tailwind.config.js
└── tsconfig.json
Enter fullscreen mode Exit fullscreen mode

Then, install and build this blog:

➜  blog-starter git:(master) ✗ yarn
➜  blog-starter git:(master) ✗ yarn build
➜  blog-starter git:(master) ✗ yarn start
Enter fullscreen mode Exit fullscreen mode

Your blog should be up and running on localhost:3000.

Build on Docker

Ignore unneeded files with .dockerignore:

node_modules
*.DS_Store
.next
.gitignore
README.md
.dockerignore
LICENSE
.docker
.gitlab
Enter fullscreen mode Exit fullscreen mode

We have 3 scenarios dockerfile in here. First, i'll use Basic Dockerfile:

➜  blog-starter git:(master) ✗ docker build -t blog-with-basic-dockerfile -f .docker/basic.dockerfile .
Enter fullscreen mode Exit fullscreen mode

basic

# Check docker images
➜  blog-starter git:(master) ✗ docker images
REPOSITORY                                       TAG                               IMAGE ID       CREATED          SIZE
blog-with-basic-dockerfile                       latest                            b70f75178890   8 seconds ago    370MB
Enter fullscreen mode Exit fullscreen mode

In this scenarios, we built nextjs image with size 370MB.

Next, we use multi-stage docker with Multi Stage Docker:

➜  blog-starter git:(master) ✗ docker build -t blog-with-multistage-dockerfile -f .docker/multistage.dockerfile .
Enter fullscreen mode Exit fullscreen mode

multi

# Check docker images
➜  blog-starter git:(master) ✗ docker images
REPOSITORY                                           TAG                               IMAGE ID       CREATED           SIZE
blog-with-multistage-dockerfile                      latest                            07c84ea2173a   38 seconds ago    339MB
Enter fullscreen mode Exit fullscreen mode

In this way, i use module node-prune in stage BUILD after install packages. node-prune can remove unnecessary files from node_modules:

         files total 43,924
       files removed 12,814
        size removed 28 MB
            duration 866ms
Enter fullscreen mode Exit fullscreen mode

Hehe, reduced 28MB. You can follow this guide for setup in your base node image. So, this images has been optimized 370MB -> 339MB.

Finally, we build dockerfile with multi-stage docker and enable mode standalone of NextJS. Create file next.config.js with content:

module.exports = {
    output: "standalone"
}
Enter fullscreen mode Exit fullscreen mode

Nextjs can automatically create a standalone folder which copies only the necessary files for a production deployment including select files in node_modules. More docs.

Let's start with Dockerfile:

➜  blog-starter git:(master) ✗ docker build -t blog-with-multistage-standalone-dockerfile -f .docker/multistage_standalone.dockerfile .
Enter fullscreen mode Exit fullscreen mode

standalone

# Check docker images
➜  blog-starter git:(master) ✗ docker images
REPOSITORY                                                      TAG                     IMAGE ID       CREATED           SIZE
blog-with-multistage-standalone-dockerfile                      latest                  07c84ea2173a   38 seconds ago    119MB
Enter fullscreen mode Exit fullscreen mode

WoW! Great ... The size of images is only 119MB.

3 images

So, I have presented 3 ways to optimize image size but ... it build very slow. In the next post, i will show you how to build image faster.

Thanks for reading 😁😁😁

💖 💪 🙅 🚩
ductnn
Duc Tran

Posted on August 5, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related