1- # Multi-stage build for production
1+ # Build stage
22FROM node:20-alpine AS builder
33
4+ # Install security updates
5+ RUN apk update && apk upgrade && apk add --no-cache dumb-init
6+
7+ # Create app user for security
8+ RUN addgroup -g 1001 -S nodejs && \
9+ adduser -S nextjs -u 1001
10+
411# Set working directory
512WORKDIR /app
613
7- # Copy package files
14+ # Copy package files for dependency caching
815COPY package*.json ./
916
10- # Install dependencies
11- RUN npm ci --only=production
17+ # Install ALL dependencies (including dev deps for TypeScript compilation)
18+ RUN npm ci --include=dev && npm cache clean --force
1219
1320# Copy source code
14- COPY . .
21+ COPY --chown=nextjs:nodejs . .
1522
1623# Build arguments for environment variables
1724ARG VITE_BASE_API_URL
@@ -21,20 +28,43 @@ ENV VITE_BASE_API_URL=$VITE_BASE_API_URL
2128RUN npm run build
2229
2330# Production stage
24- FROM nginx:alpine
31+ FROM nginx:1.25-alpine AS runtime
32+
33+ # Install security updates and curl for health checks
34+ RUN apk update && apk upgrade && \
35+ apk add --no-cache curl dumb-init && \
36+ rm -rf /var/cache/apk/*
37+
38+ # Create non-root user
39+ RUN addgroup -g 1001 -S nginx-app && \
40+ adduser -S nginx-app -u 1001 -G nginx-app
2541
2642# Copy built files from builder stage
27- COPY --from=builder /app/dist /usr/share/nginx/html
43+ COPY --from=builder --chown=nginx-app:nginx-app /app/dist /usr/share/nginx/html
2844
2945# Copy nginx configuration
30- COPY nginx.conf /etc/nginx/conf.d/default.conf
46+ COPY --chown=nginx-app:nginx-app nginx.conf /etc/nginx/conf.d/default.conf
3147
32- # Expose port 80
33- EXPOSE 80
48+ # Set proper permissions
49+ RUN chown -R nginx-app:nginx-app /usr/share/nginx/html && \
50+ chown -R nginx-app:nginx-app /var/cache/nginx && \
51+ chown -R nginx-app:nginx-app /var/log/nginx && \
52+ chown -R nginx-app:nginx-app /etc/nginx/conf.d && \
53+ touch /var/run/nginx.pid && \
54+ chown -R nginx-app:nginx-app /var/run/nginx.pid
55+
56+ # Switch to non-root user
57+ USER nginx-app
58+
59+ # Expose port 8080 (non-privileged port)
60+ EXPOSE 8080
3461
3562# Health check
3663HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
37- CMD curl -f http://localhost/ || exit 1
64+ CMD curl -f http://localhost:8080/health || curl -f http://localhost:8080/ || exit 1
65+
66+ # Use dumb-init to handle signals properly
67+ ENTRYPOINT ["dumb-init" , "--" ]
3868
3969# Start nginx
4070CMD ["nginx" , "-g" , "daemon off;" ]
0 commit comments