# syntax=docker/dockerfile:1.7 FROM node:24-alpine AS api-deps WORKDIR /build/api COPY api/package*.json ./ RUN --mount=type=cache,target=/root/.npm npm ci FROM node:24-alpine AS api-build WORKDIR /build/api COPY --from=api-deps /build/api/node_modules ./node_modules COPY api/package*.json ./ COPY api/nest-cli.json api/tsconfig*.json ./ COPY api/src ./src RUN npm run build FROM node:24-alpine AS api-prod-deps WORKDIR /build/api ENV NODE_ENV=production COPY api/package*.json ./ RUN --mount=type=cache,target=/root/.npm npm ci --omit=dev --ignore-scripts FROM node:24-alpine AS client-deps WORKDIR /build/client COPY client/package*.json ./ RUN --mount=type=cache,target=/root/.npm npm ci FROM node:24-alpine AS client-build WORKDIR /build/client COPY --from=client-deps /build/client/node_modules ./node_modules COPY client/package*.json ./ COPY client/angular.json client/tsconfig*.json ./ COPY client/public ./public COPY client/src ./src RUN npm run build FROM node:24-alpine AS production WORKDIR /app ENV NODE_ENV=production ENV PORT=3000 RUN apk add --no-cache nginx supervisor wget \ && mkdir -p /run/nginx /var/log/supervisor /usr/share/nginx/html \ && chown -R node:node /app COPY --from=api-prod-deps /build/api/node_modules ./api/node_modules COPY --from=api-build /build/api/dist ./api/dist COPY api/package*.json ./api/ COPY --from=client-build /build/client/dist/client/browser /usr/share/nginx/html RUN <<'EOF' cat > /etc/nginx/http.d/default.conf <<'NGINX' server { listen 80; server_name _; root /usr/share/nginx/html; index index.html; access_log /dev/stdout; error_log /dev/stderr warn; gzip on; gzip_comp_level 5; gzip_min_length 1024; gzip_types application/javascript application/json image/svg+xml text/css text/plain; location ~* \.(?:css|js|mjs|png|jpg|jpeg|gif|ico|svg|webp|woff2?)$ { access_log off; add_header Cache-Control "public, max-age=31536000, immutable"; try_files $uri =404; } location ~ ^/(analytics|auth|strava)(/|$) { proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Real-IP $remote_addr; proxy_pass http://127.0.0.1:3000; } location / { add_header Cache-Control "no-store"; try_files $uri $uri/ /index.html; } } NGINX cat > /etc/supervisord.conf <<'SUPERVISOR' [supervisord] nodaemon=true logfile=/dev/null logfile_maxbytes=0 pidfile=/tmp/supervisord.pid [program:api] directory=/app/api command=node dist/main.js user=node autorestart=true stopasgroup=true killasgroup=true stdout_logfile=/dev/fd/1 stdout_logfile_maxbytes=0 stderr_logfile=/dev/fd/2 stderr_logfile_maxbytes=0 [program:nginx] command=nginx -g "daemon off;" autorestart=true stopasgroup=true killasgroup=true stdout_logfile=/dev/fd/1 stdout_logfile_maxbytes=0 stderr_logfile=/dev/fd/2 stderr_logfile_maxbytes=0 SUPERVISOR EOF HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \ CMD wget -qO- http://127.0.0.1/ >/dev/null || exit 1 EXPOSE 80 CMD ["supervisord", "-c", "/etc/supervisord.conf"]