# ────────────────────────────────────────────────────────────────────────────── # Exocore Docs — production Docker image # # Two-stage build: # 1. Node 20 builds the Vite + React SPA into ./dist # 2. nginx-alpine serves the static bundle (no Node runtime in the # final image — keeps it tiny: ~25 MB compressed) # # Build: # docker build -t exocore-docs:latest -f exocore-docs/Dockerfile exocore-docs # # Run (host port 8080 → container port 80): # docker run --rm -p 8080:80 exocore-docs:latest # # Hugging Face Spaces: this image works as-is on a "Docker" Space — HF # expects something on port 7860, so override at run-time: # docker run -p 7860:7860 -e PORT=7860 exocore-docs:latest # ────────────────────────────────────────────────────────────────────────────── # ───── Stage 1: build the SPA ───── FROM node:20-bookworm-slim AS builder WORKDIR /build # Cache npm install on the lockfile. COPY package*.json ./ RUN npm install --legacy-peer-deps # Pull in the rest of the project. COPY tsconfig.json vite.config.ts index.html ./ COPY public ./public COPY docs ./docs COPY src ./src RUN npm run build # ───── Stage 2: static serve via nginx ───── FROM nginx:1.27-alpine AS runtime # Drop the default nginx site and replace it with one that listens on # $PORT (defaults to 80) and serves the SPA with hash-routing fallback. RUN rm /etc/nginx/conf.d/default.conf # Create the templates directory before writing to it RUN mkdir -p /etc/nginx/templates # Inline nginx config — kept here so the image is reproducible without # any extra files outside the Docker build context. RUN printf '%s\n' \ 'server {' \ ' listen ${NGINX_PORT};' \ ' server_name _;' \ ' root /usr/share/nginx/html;' \ ' index index.html;' \ '' \ ' # Long cache on hashed assets, short cache on the entry HTML.' \ ' location /assets/ {' \ ' add_header Cache-Control "public, max-age=31536000, immutable";' \ ' try_files $uri =404;' \ ' }' \ ' location /screenshots/ {' \ ' add_header Cache-Control "public, max-age=86400";' \ ' try_files $uri =404;' \ ' }' \ '' \ ' # Hash-router SPA fallback — every unknown path returns index.html.' \ ' location / {' \ ' add_header Cache-Control "no-cache";' \ ' try_files $uri $uri/ /index.html;' \ ' }' \ '' \ ' gzip on;' \ ' gzip_types text/plain text/css application/javascript application/json image/svg+xml;' \ ' gzip_min_length 1024;' \ '}' \ > /etc/nginx/templates/default.conf.template COPY --from=builder /build/dist /usr/share/nginx/html # Default to Hugging Face's required port ENV PORT=7860 ENV NGINX_PORT=7860 # Expose the exact port Hugging Face looks for EXPOSE 7860 CMD ["sh", "-c", "export NGINX_PORT=${PORT:-7860}; exec nginx -g 'daemon off;'"]