| # βββββββββββββββββββββββββββββββββββββββββββββ | |
| # Stage 1 β Build the React frontend | |
| # βββββββββββββββββββββββββββββββββββββββββββββ | |
| FROM node:20-alpine AS frontend-builder | |
| WORKDIR /app/frontend | |
| # Install dependencies first (cache-friendly) | |
| COPY frontend/package*.json ./ | |
| RUN npm ci | |
| # Copy source and build | |
| COPY frontend/ ./ | |
| RUN npm run build | |
| # βββββββββββββββββββββββββββββββββββββββββββββ | |
| # Stage 2 β Build the Go backend | |
| # βββββββββββββββββββββββββββββββββββββββββββββ | |
| FROM golang:1.21-alpine AS backend-builder | |
| WORKDIR /app/backend | |
| # Copy go module files first for caching | |
| COPY backend/go.mod backend/go.sum ./ | |
| RUN go mod download | |
| # Copy source and build a static binary | |
| COPY backend/ ./ | |
| RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /portfolio-api ./cmd/main.go | |
| # βββββββββββββββββββββββββββββββββββββββββββββ | |
| # Stage 3 β Final image with Nginx + Go binary | |
| # βββββββββββββββββββββββββββββββββββββββββββββ | |
| FROM nginx:1.25-alpine | |
| # Install supervisord to run two processes | |
| RUN apk add --no-cache supervisor | |
| # Copy built frontend into Nginx web root | |
| COPY --from=frontend-builder /app/frontend/dist /usr/share/nginx/html | |
| # Copy compiled Go binary | |
| COPY --from=backend-builder /portfolio-api /app/portfolio-api | |
| # Copy Nginx config | |
| COPY nginx.conf /etc/nginx/nginx.conf | |
| # Copy supervisord config | |
| COPY supervisord.conf /etc/supervisord.conf | |
| # Hugging Face Spaces injects a random non-root UID at runtime. | |
| # We can't use USER 1000 because that UID may not exist in /etc/passwd. | |
| # Instead we make all writable paths accessible to any UID. | |
| RUN mkdir -p /var/cache/nginx /var/log/nginx /var/run /tmp/nginx \ | |
| && chmod -R 777 /var/cache/nginx /var/log/nginx /var/run /tmp/nginx \ | |
| && chmod -R 755 /usr/share/nginx/html \ | |
| && chmod +x /app/portfolio-api \ | |
| # Allow nginx to write its PID to /tmp (world-writable) instead of /var/run | |
| && sed -i 's|/var/run/nginx.pid|/tmp/nginx/nginx.pid|g' /etc/nginx/nginx.conf 2>/dev/null || true | |
| EXPOSE 7860 | |
| CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"] | |