Comparing next start and next standalone with docker
I wrote about how to use nextjs with docker.
I wanted to compare using next standalone
like in the article and just using next start
.
I describe the new docker file I used for using next start
in docker at the bottom of this article. But let's jump straight to the learnings.
Docker Image Size
The standalone mode I used in my original article creates a docker container of 230MB.
The next start
mode creates a docker container of 750MB.
If you're passing around many docker containers throughout the day the lower size of standalone mode can be a big difference.
Static Asset serving
Standalone next build provides you a separate set of static assets that you can use in AWS S3 or similar instead of your docker container.
This is a big advantage of standalone mode if you have many static assets or your site is very busy.
Conclusion
The next start
method is easier to setup in docker. It's also the default way to run nextjs and will always support all next features.
The standalone method is a bit more complex to setup in docker but once you set it up it doesn't change very often.
The standalone image is less than half the size of the full app method.
I'm going to continue using standalone for now!
Appendix: The next start dockerfile used
To setup the test we use the following dockerfile:
# A container with pnpm and python3 is required
FROM node:18-alpine as pnpm_base
WORKDIR /app
RUN npm i --global --no-update-notifier --no-fund pnpm@7
RUN apk add --no-cache g++ make py3-pip libc6-compat
# run fetch in a separate step to avoid re-fetching deps on every change
FROM pnpm_base as fetched_deps
WORKDIR /app
ENV NODE_ENV production
COPY pnpm-lock.yaml ./
RUN pnpm config set store-dir /workdir/.pnpm-store
RUN pnpm fetch
# install all deps from cache
FROM fetched_deps as with_all_deps
COPY . ./
RUN pnpm install --offline
# Build the BE
# the main issue with building everything is the FE depends on a running BE
FROM with_all_deps as builder
RUN pnpm --filter='*frontend' build
RUN pnpm --filter='*frontend' deploy pruned --prod
# Production image - only take pruned assets
FROM node:18-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
RUN addgroup --system --gid 1001 app
RUN adduser --system --uid 1001 app
USER app
COPY /app/apps/frontend/ /
EXPOSE 5000
ENV PORT 5000
CMD ["npm", "start"]
Here is another example using yarn
FROM node:18-alpine as base
FROM base as with_all_deps
WORKDIR /app
COPY . ./
RUN yarn
RUN yarn build
RUN yarn cache clean --all
FROM node:18-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
RUN addgroup --system --gid 1001 app
RUN adduser --system --uid 1001 app
USER app
COPY /app ./
EXPOSE 5000
ENV PORT 5000
CMD ["yarn", "start"]