Docker image optimization with output file tracing

We run our Next webapps on a kubernetes cluster. There’s a whole lot of infrastructure for some webapps these days. You know how it goes.

Since we run on a kubernetes cluster, we package our next webapps into docker images. Doing this the naive way uses the following steps:

  1. install all dependencies (dependencies + dev dependencies)
  2. yarn build to build the .next folder
  3. delete the node_modules
  4. install only dependencies (no dev dependencies)
  5. copy .next, node_modules and other meta files package.json, yarn.lock

At the end of all this, you might end up with a 500MB+ sized docker image. Being a frontend and a backend, you have more dependencies compared to an SPA (usually, sometimes). Meanwhile, there are tons of support files bundled togther in the image. Things that aren’t necessary for the Next webapp to run either.

That’s where the Output File Tracing feature comes in. I think Standalone mode better a better name. Output File Tracing can produce the standalone folder with everything you need to run your Next webapp.

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.

Set your next.config.js:

module.exports = {
    // ...
    output: 'standalone',
}

Then update your Dockerfile to use the new standalone structure. These details are mentioned, but I thought they were unclear without an example Dockerfile:

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

Additionally, a minimal server.js file is also output which can be used instead of next start. This minimal server does not copy the public or .next/static folders by default as these should ideally be handled by a CDN instead, although these folders can be copied to the standalone/public and standalone/.next/static folders manually, after which server.js file will serve these automatically.

Note: next.config.js is read during next build and serialized into the server.js output file. If the legacy serverRuntimeConfig or publicRuntimeConfig options are being used, the values will be specific to values at build time.

Here’s my updated Dockerfile

FROM node:16-alpine

WORKDIR /app

ENV NODE_ENV="production"

# ---

COPY ./public ./public

# Automatically leverage output traces to reduce image size
# https://nextjs.org/docs/advanced-features/output-file-tracing
COPY ./.next/standalone ./
COPY ./.next/static ./.next/static

CMD [ "node", "./server.js" ]

The docker image size reduction is huge. Going from 500MB+ to a much smaller 100MB.

Follow me on Mastodon @ryanmr@mastodon.cloud.

Follow me on Twitter @ryanmr.