# syntax=docker/dockerfile:1 # ---------- Build stage ---------- FROM node:20-alpine AS builder WORKDIR /app # Install Yarn 1 (classic) – ensures compatibility with existing lockfile RUN if command -v yarn >/dev/null && [ "$(yarn --version)" = "1.22.19" ]; then \ echo "Yarn 1.22.19 already installed"; \ else \ rm -f /usr/local/bin/yarn /usr/local/bin/yarnpkg && \ npm install -g yarn@1.22.19 && yarn --version; \ fi # Install dependencies (cache layer) COPY package.json . COPY yarn.lock . RUN yarn install --production=false # Copy source code COPY . . # Prune dev dependencies for production image RUN yarn install --production && \ rm -rf **/*.test.js # ---------- Runtime stage ---------- FROM node:20-alpine AS runtime RUN addgroup -S appgroup && adduser -S appuser -G appgroup WORKDIR /app COPY --from=builder /app/package.json . COPY --from=builder /app/node_modules ./node_modules COPY --from=builder /app/src ./src ENV NODE_ENV=production USER appuser EXPOSE 3000 HEALTHCHECK --interval=30s --timeout=5s --start-period=5s \ CMD node -e "require('http').get('http://localhost:3000/health', () => process.exit(0)).on('error', () => process.exit(1))" CMD ["node", "src/index.js"]