DEV Community

Goffity Corleone
Goffity Corleone

Posted on • Edited on

Reduce docker image size of nextJS

I had the opportunity to work on a legacy project developed in React with nextJS framework. I am responsible for building the docker container and deploying it to the cluster. When I looked at a Dockerfile and make it, the size of the image was huge.

Docker images huge


The legacy Dockerfile are

FROM node:16

WORKDIR /app
COPY . .
RUN yarn install --frozen-lockfile
CMD ["yarn","start"]
Enter fullscreen mode Exit fullscreen mode

I got the first problem. In the Dockerfile.

First Solution

I try to use multi-stage build for reduce docker images size.

FROM node:16 AS dependency

WORKDIR /app
COPY package.json .
COPY yarn.lock .
RUN yarn install --frozen-lockfile

FROM node:16-alpine AS builder
WORKDIR /app
COPY --from=dependency /app/node_modules ./node_modules
COPY . .
RUN env
RUN yarn build

CMD [ "yarn","start" ]
Enter fullscreen mode Exit fullscreen mode

But the size of the image is still not satisfactory.

Docker images smaller

Second

I try to research the way to build and deploy nextjs application with docker images and I found the solution on next.js Github

  1. Use Output Standalone Edit next.config.js
module.exports = {
  // ... rest of the configuration.
  output: 'standalone',
}
Enter fullscreen mode Exit fullscreen mode
  1. Edit Dockerfile follow example.

FROM node:16-alpine AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /app
COPY yarn.lock* package-lock.json* pnpm-lock.yaml* ./
RUN \
  if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
  elif [ -f package-lock.json ]; then npm ci; \
  elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && pnpm i; \
  else echo "Lockfile not found." && exit 1; \
  fi

FROM node:16-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN yarn build

FROM node:16-alpine AS runner
WORKDIR /app
ENV NODE_ENV production
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
ENV PORT 3000
CMD ["node", "server.js"]

Enter fullscreen mode Exit fullscreen mode
  1. Ignore unnecessary files, I add Added .dockerignore to ignore that.
node_modules
build
.dockerignore
Dockerfile
Dockerfile.*
Enter fullscreen mode Exit fullscreen mode

Result

The image size reduce from over 1GB to around 1xxMB

Final

Use cache

In this project we use yarn as package dependency. and in the Dockerfile if we run

yarn --frozen-lockfile
Enter fullscreen mode Exit fullscreen mode

take a very long time so I changed a Dockerfile to use cache on package dependency.

FROM node:16-alpine AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /app
COPY yarn.lock* package-lock.json* pnpm-lock.yaml* ./
# RUN \
#   if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
#   elif [ -f package-lock.json ]; then npm ci; \
#   elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && pnpm i; \
#   else echo "Lockfile not found." && exit 1; \
#   fi
RUN --mount=type=cache,target=/root/.yarn YARN_CACHE_FOLDER=/root/.yarn yarn install --frozen-lockfile

...
Enter fullscreen mode Exit fullscreen mode

Top comments (3)

Collapse
 
ajeetraina profile image
Ajeet Singh Raina

One typo - It's called Multi-Stage build, not multi-state build.

Collapse
 
goffity profile image
Goffity Corleone

Thanks @ajeetraina this is my mistake.

Collapse
 
nduyvu1511 profile image
Vu

my app was 6.72GB before and now just 162MB, thanks a lot