Multi-Stage Builds
Multi-stage Dockerfiles let you create different images from the same Dockerfile, each optimized for its purpose.
The API Dockerfile
Section titled “The API Dockerfile”Our API uses a four-stage Dockerfile:
cat api/Dockerfile# Base stage with common setupFROM node:22-alpine AS baseWORKDIR /appRUN apk add --no-cache curl
# Development stage - includes dev dependencies and toolsFROM base AS developmentCOPY package.json ./RUN npm installCOPY . ./ENV NODE_ENV=developmentEXPOSE 3000CMD ["npm", "run", "dev"]
# Production build stageFROM base AS builderCOPY package.json ./RUN npm installCOPY tsconfig.json ./COPY src ./srcRUN npm run build
# Production stage - minimal imageFROM base AS productionCOPY package.json ./RUN npm install --omit=dev && npm cache clean --forceCOPY --from=builder /app/dist ./distENV NODE_ENV=productionEXPOSE 3000USER nodeCMD ["node", "dist/server.js"]Understanding the Stages
Section titled “Understanding the Stages”Common foundation - Node.js with curl for health checks.
development
Section titled “development”- Full
npm install(includes devDependencies) - Hot reload with
npm run dev - Optimized for developer experience
builder
Section titled “builder”Intermediate stage that compiles TypeScript to JavaScript. Not used directly.
production
Section titled “production”- Production-only dependencies (
npm install --omit=dev) - Compiled assets from builder
- Runs as non-root user (
USER node) - Minimal image size
Specifying the Target
Section titled “Specifying the Target”In your Embfile, set which stage to build:
resources: image: type: docker/image params: target: development # or productionThe Web Dockerfile
Section titled “The Web Dockerfile”The web component has a simpler two-stage setup:
cat web/Dockerfile# Development stage with live reloadFROM nginx:alpine AS developmentCOPY index.html /usr/share/nginx/html/COPY nginx.conf /etc/nginx/conf.d/default.confEXPOSE 80CMD ["nginx", "-g", "daemon off;"]
# Production stage with optimizationsFROM nginx:alpine AS productionCOPY index.html /usr/share/nginx/html/COPY nginx.prod.conf /etc/nginx/conf.d/default.confRUN rm -rf /usr/share/nginx/html/*.map 2>/dev/null || trueEXPOSE 80CMD ["nginx", "-g", "daemon off;"]Key differences:
- development: Uses
nginx.confwith debugging features - production: Uses
nginx.prod.confwith optimizations, removes source maps
Default Target
Section titled “Default Target”The project configuration sets a default target:
defaults: docker: tag: ${env:DOCKER_TAG} target: developmentAll components use development by default. Flavors can override this.
Next Step
Section titled “Next Step”Continue to Flavors Introduction to learn how to switch between development and production builds.