# Multi-stage build for production-ready Etsy Finance Tracker with Nginx # Stage 1: Build the React client FROM node:18-alpine AS client-build WORKDIR /app/client # Copy client package files COPY client/package*.json ./ RUN npm ci # Copy client source and build COPY client/ ./ RUN npm run build # Stage 2: Build the Node.js server FROM node:18-alpine AS server-build WORKDIR /app/server # Copy server package files COPY server/package*.json ./ RUN npm ci # Copy server source and build COPY server/ ./ RUN npm run build # Stage 3: Production API server (no static files) FROM node:18-alpine AS production WORKDIR /app # Install dumb-init for proper signal handling and curl for health checks RUN apk add --no-cache dumb-init curl # Create non-root user for security RUN addgroup -g 1001 -S nodejs RUN adduser -S nodejs -u 1001 # Copy server package files for production dependencies COPY --from=server-build --chown=nodejs:nodejs /app/server/package*.json ./server/ # Install only production dependencies RUN cd server && npm ci --only=production && cd .. # Copy built server COPY --from=server-build --chown=nodejs:nodejs /app/server/dist ./server/dist/ # Copy built client for nginx sharing COPY --from=client-build --chown=nodejs:nodejs /app/client/dist ./client/dist/ # Create data directory for persistent storage RUN mkdir -p /app/data && chown nodejs:nodejs /app/data # Switch to non-root user USER nodejs # Expose API port (nginx will handle port 80) EXPOSE 8080 # Health check for API server HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ CMD curl -f http://localhost:8080/health || exit 1 # Start the API server ENTRYPOINT ["dumb-init", "--"] CMD ["node", "server/dist/index.js"] # Stage 4: Nginx server with pre-built client FROM nginx:alpine AS nginx-frontend COPY --from=client-build /app/client/dist /usr/share/nginx/html COPY nginx.deploy.conf /etc/nginx/nginx.conf