Skip to content

Instantly share code, notes, and snippets.

@matzar
Last active November 29, 2023 11:41
Show Gist options
  • Save matzar/181f39b917bbc8e7a7a60556c3a987b0 to your computer and use it in GitHub Desktop.
Save matzar/181f39b917bbc8e7a7a60556c3a987b0 to your computer and use it in GitHub Desktop.
Multi-stage build Dockerfile for a Node.js application that uses the NX workspace and Nginx as a web server for the runtime environment.
# Use a specific version of the official Node.js image based on the slim variant
FROM node:18.16.0-bullseye-slim AS build
# Set the working directory
WORKDIR /usr/src/app
# Copy required files for installing dependencies
COPY package.json package-lock.json nx.json tsconfig.base.json decorate-angular-cli.js ./
COPY apps/<app-name>/ ./apps/<app-name>/
COPY apps/<app-name>-e2e/ ./apps/<app-name>-e2e/
# Install npm dependencies using a secret .npmrc file
RUN --mount=type=secret,id=secret_npmrc,target=/root/.npmrc npm ci
# Set NX_DAEMON environment variable to false to prevent nx from running in daemon mode
ENV NX_DAEMON=false
# Build the application using the locally installed Nx
RUN npx nx run <app-name>:build
# Create a new stage for the runtime image
FROM nginx:1.25.3-alpine AS runtime
# Set the working directory
WORKDIR /usr/share/nginx/html
# USER root: Copy build artifacts from the build stage
COPY --from=build /usr/src/app/dist/apps/<app-name> .
# USER root: Copy the nginx configuration file
COPY apps/<app-name>/nginx.conf /etc/nginx/conf.d/default.conf
# Expose ports
EXPOSE 8100 8090 8080
# Start the application using Nginx
CMD ["nginx", "-g", "daemon off;"]
@ikosta
Copy link

ikosta commented May 19, 2023

@matzar this looks like the config for an Angular (web) application and not a Node.js (server) application.

For an Angular apps this looks pretty fine but you may want to change the order of the Docker commands to get the most out of the Docker cache.

What changes often should be to the bottom of every stage and what changes less to the top. ENV in example would never change so put it to the top. Also the nginx.conf will not be changed more than the /usr/src/app/dist/apps/<app-name>. So copying /usr/src/app/dist/apps/<app-name> after the nginx.conf would use the cache more often.

You can also use npx nx run <app-name>:build so you don't have to install nx. We actually have a build image with more dependencies installed that is used as builder. These dependencies are not changed very often and this build image is build beforehand.

We build this image when Node.js is updated or we update npm or nx.
Pinning nx version is pretty necessary since you need to run migrations when updating.

FROM node:18-slim

ENV NX_DAEMON=false

RUN apt-get update && apt-get install -y \
  g++ \
  git \
  make \
  openssl \
  python3 \
  && rm -rf /var/lib/apt/lists/*

RUN npm -g i npm@9.6.5
RUN npm -g i nx@15.8.9

As you will continue with more dependencies you will need also more native ones and you don't need to update several Dockerfile`s this way. Especially when working with Nx.dev.

@matzar
Copy link
Author

matzar commented May 22, 2023

Thanks a lot for this @ikosta . Very valuable feedback indeed! Especially about putting your not-so-frequently-used dependencies into their own image. I've heard some people doing it and it's definitely something that could speed up the process significantly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment