Slimming Down node_modules in Docker Images

February 04, 20191 minute

Everyone knows the meme about the size of the node_modules folder so I'll try to make this one quick. You can (in some cases) drastically reduce the size of a resulting docker image by avoiding the inclusion of non-production node modules.

Create a builder image

This is an easy one, and probably something you're already used to doing. Create a Dockerfile as you would normally but this time we'll add an as keyword. This essentially creates a name for the current image in the FROM line.

FROM node:8-alpine as builderWORKDIR /app

COPY src/ ./src
COPY package* ./
COPY tsconfig.json ./

RUN npm install
RUN npm run build

Install your production dependencies

We're also going to use the same as keyword in this image to tell the final image where to get the production dependencies from.

FROM node:10-alpine as dependenciesWORKDIR /app

# Copy over the original package.json, we need this to npm install!
COPY --from=builder /app/package* ./

RUN npm install --production

Compose your final image

Now we can reference the previous images using the --from argument * gasps *. This means we can compose a final image from all the previous stages without the node_modules bloat.

FROM node:10-alpine
WORKDIR /app

# Copy over our built application code
COPY --from=builder /app/dist ./dist
# Copy over the original package.json so we know how to start it
COPY --from=builder /app/package.json .
# Copy over our hopefully much lighter node_modules folder
COPY --from=dependencies /app/node_modules ./node_modules
CMD ["npm", "run", "start"]

Putting all three snippets together in a single Dockerfile should achieve a much thinner image now that we're not including all our development dependencies as well.

For more about multi-stage builds, see the official Docker documentation.

This is a preview of a simpler page design that I'm working on over the next little bit. I've finally added a (click it!) but there's still a few pages left to be converted so don't worry if things don't look quite right just yet 🙏

Content on blog pages use the CC-BY-SA license. The source code and notes use the MIT license. Unsure? Mention me on Mastodon.