Containerize your nextJS Application: the right way

For most of time, i deployed react app or nextJs on cloudflare pages or with nextJs is Vercel.But today I try to deploy it to k8s. And WTF my nextJs application’s docker image cost more than 4GB in hard disk. Then I try to optimize it and then my image just cost 300 MBs so I think it should be the right way. So in this blog let’s see how we can containerize our nextJs App using Docker.

The straight forward way

In local development what do you do when you need to run a production build of a nextJs Application ?. Something like that right:

npm install
npm run build
npm start

So This maybe the Dockerfile that you can imagine

FROM node-18:alpine
WORKDIR /app
COPY package.json .
RUN npm install
RUN npm run build
EXPOSE 3000
CMD  ["npm", "start"]

Simple and straight forward right ? Yes it works but wtf is this

Total docker image cause about more than 4Gbs and there is an layer with 4.14GB :((( this is log when i try to push my image to my private registry LOL :)))

Actually base on your npm dependencies or source code, public assets (images, videos,…) your images size maybe not increase to 4GB like me but i think it will be around 500mb to 1GB it still to big for a production build to deploy on k8s or anywhere.

So why ? Here is quick explanation

=))) yep by create Dockerfile like the node_modules layer is link to next layer so it costs much disk storage

Using docker multi-stage and nextJs standalone mode

Docker multi-stage builds are a technique for optimizing Docker images by using multiple FROM statements in a single Dockerfile. This allows you to use different base images for different stages of the build process, significantly reducing the final image size.

  • Multi-stage builds streamline the creation of Docker images by allowing you to copy only the necessary artifacts from one stage to another, eliminating unnecessary dependencies and files.
  • You define multiple stages in your Dockerfile using multiple FROM statements. Each stage can use a different base image and perform specific tasks. At the end of the build, only the required parts from intermediate stages are included in the final image.
  • The primary benefits are smaller image sizes, improved security by minimizing attack surface, and faster build times due to reduced complexity and fewer dependencies.

Next.js standalone mode allows you to build a fully self-contained application with all necessary dependencies bundled into a single directory. This makes it easier to deploy and run your Next.js app in various environments without needing the entire development setup.https://nextjs.org/docs/pages/api-reference/next-config-js/output

Ok enough theory here is how you can optimize your docker file using multi-stage buils with nextjs standalone export

#build stage
FROM node:18-alpine as builder
WORKDIR /app

COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build

# production stage
FROM node:18-alpine as runner
WORKDIR /app
COPY --from=builder /app/package.json .
COPY --from=builder /app/package-lock.json .
COPY --from=builder /app/next.config.js ./
COPY --from=builder /app/public ./public
# standalone mode which node require node_modules/ (reduce image size) read more https://nextjs.org/docs/pages/api-reference/next-config-js/output
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
EXPOSE 3000
ENV NEXT_SHARP_PATH=node_modules/sharp
ENTRYPOINT ["node", "server.js"]

For this docker file to work you must sepecify next to use standalone export mode. Find the next.config.js file and edit it like that

const nextConfig = {
    output: 'standalone',
};

module.exports = nextConfig;

Then

docker build -t my-next-app .
docker run -it -p 3000:3000 my-next-app

Now you can get your app running and if you type docker images you can see your image must around 100 to 300Mbs.

The second image is the one built with the dockerfile in first section and the first image using multi-stages build and nextjs stand-alone export. You can see how I optimized my image from ~5GB to 300MB LOL :)))

Conclusion

The multi-stages build concept is not use to optimize nextJs only. With node_modules/ supper big I think each nodeJs project must separate the install and build steps in docker file to reduce the image size. Ok so in this blog you know how to build an optimized docker image for nextJs Application. Thanks for reading and happy deploying things

__CodingCat__

Leave a Reply

Your email address will not be published. Required fields are marked *