Small Image, Multi-Stage Build
Dockerizing Fastify
A multi-stage Dockerfile builds with dev dependencies and ships only production deps for a small, fast image.
What you'll learn
- Write a multi-stage Dockerfile
- Use node-slim or alpine base images
- Add HEALTHCHECK for orchestrators
A Fastify image should be small, reproducible, and run as a non-root user. The shape that works almost everywhere is a multi-stage Dockerfile: build with dev deps, ship with prod deps.
Multi-Stage Dockerfile
FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:20-alpine
WORKDIR /app
ENV NODE_ENV=production
COPY package*.json ./
RUN npm ci --omit=dev
COPY --from=build /app/dist ./dist
HEALTHCHECK --interval=10s --timeout=3s \
CMD wget -qO- http://localhost:3000/healthz || exit 1
USER node
CMD ["node", "dist/index.js"] The build stage compiles TypeScript with dev dependencies. The runtime stage installs only
production deps and copies dist/, leaving a slim image (~150 MB on alpine).
.dockerignore
Without this, you’ll bake node_modules, .git, and secrets into the image.
# .dockerignore
node_modules
.git
.env
.env.*
dist
coverage Build and Run
docker build -t my-api .
docker run --rm -p 3000:3000 --env-file .env my-api For local dev, use a separate Dockerfile.dev with tsx --watch and a bind mount — keep
production images lean.