# Build stage FROM node:20-alpine AS builder WORKDIR /app # Copy package files first for layer caching COPY package.json package-lock.json* ./ # Install all dependencies for build RUN npm ci # Copy source files COPY . . # Build the frontend RUN npm run build # Production stage FROM node:20-alpine AS runner WORKDIR /app # Create non-root user for security (consistent UID/GID 1001) RUN addgroup -g 1001 -S nodejs && \ adduser -S nodejs -u 1001 -G nodejs # Set environment ENV NODE_ENV=production ENV SERVER_PORT=3001 ENV RATE_LIMIT_PER_MINUTE=5 ENV CORS_ORIGIN=* ENV LOG_LEVEL=info ENV ZOHO_ENABLED=false ENV ZOHO_API_DOMAIN=https://www.zohoapis.com ENV ZOHO_CLIENT_ID= ENV ZOHO_CLIENT_SECRET= ENV ZOHO_REFRESH_TOKEN= ENV ZOHO_REDIRECT_URI= # Create app directory structure RUN mkdir -p /app/db /app/logs # Copy entrypoint script COPY docker-entrypoint.sh /usr/local/bin/ RUN chmod +x /usr/local/bin/docker-entrypoint.sh # Copy from builder - built artifacts and package manifests COPY --from=builder /app/package.json /app/package-lock.json* ./ COPY --from=builder /app/dist ./dist COPY --from=builder /app/server ./server # Install production dependencies only in runtime stage RUN npm ci --omit=dev # Install su-exec for switching to non-root user RUN apk add --no-cache su-exec && \ rm -rf /var/cache/apk/* # Expose backend port EXPOSE 3001 # Health check using Node 20 built-in fetch (no wget required) HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \ CMD node -e "fetch('http://localhost:3001/api/health').then(r => r.ok ? 0 : 1).catch(() => 1)" || exit 1 # Run the Express server via entrypoint (runs as root, then switches to nodejs) ENTRYPOINT ["docker-entrypoint.sh"] CMD ["node", "server/index.js"]