From 029fbae471dca8753f1d7878f861bd09b9207910 Mon Sep 17 00:00:00 2001 From: Julien Calixte Date: Mon, 1 Jun 2026 21:41:21 +0200 Subject: [PATCH] chore(deploy): add Dockerfile and nginx config for Coolify SPA hosting Multi-stage build: node:22-alpine + pnpm builds, nginx:1.27-alpine serves. VITE_* env vars are passed as build args because Vite inlines them into the JS bundle at build time. nginx config does SPA fallback, caches /assets/* immutably, and forces no-store on sw.js/index.html so PWA updates propagate. --- .dockerignore | 14 ++++++++++++ Dockerfile | 34 +++++++++++++++++++++++++++++ nginx.conf | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 107 insertions(+) create mode 100644 .dockerignore create mode 100644 Dockerfile create mode 100644 nginx.conf diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..f9fba37 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,14 @@ +.git +.gitignore +node_modules +dist +.DS_Store +.env +.env.* +!.env.example +.vscode +.idea +*.log +README.md +src-legacy +tests/examples diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..c83efde --- /dev/null +++ b/Dockerfile @@ -0,0 +1,34 @@ +# syntax=docker/dockerfile:1.7 + +# -------- Build stage -------- +FROM node:22-alpine AS builder +WORKDIR /app + +# pnpm via corepack — pin the version that matches package.json +RUN corepack enable && corepack prepare pnpm@11.5.0 --activate + +# Install deps first (cached as long as the lockfile is unchanged) +COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ +RUN pnpm install --frozen-lockfile + +# Build-time env (Vite inlines VITE_* into the bundle) +ARG VITE_COUCHDB +ARG VITE_MAPBOX_KEY +ARG VITE_MAP_KEY +ENV VITE_COUCHDB=$VITE_COUCHDB \ + VITE_MAPBOX_KEY=$VITE_MAPBOX_KEY \ + VITE_MAP_KEY=$VITE_MAP_KEY + +COPY . . +RUN pnpm build + +# -------- Runtime stage -------- +FROM nginx:1.27-alpine + +COPY nginx.conf /etc/nginx/conf.d/default.conf +COPY --from=builder /app/dist /usr/share/nginx/html + +EXPOSE 80 + +HEALTHCHECK --interval=30s --timeout=3s --start-period=5s \ + CMD wget -qO- http://127.0.0.1/ >/dev/null || exit 1 diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 0000000..b35e986 --- /dev/null +++ b/nginx.conf @@ -0,0 +1,59 @@ +server { + listen 80; + server_name _; + + root /usr/share/nginx/html; + index index.html; + + # Compression + gzip on; + gzip_comp_level 5; + gzip_min_length 256; + gzip_proxied any; + gzip_vary on; + gzip_types + application/javascript + application/json + application/manifest+json + application/wasm + application/xml + font/woff + font/woff2 + image/svg+xml + text/css + text/plain; + + # Vite emits hashed file names under /assets/ — cache hard + location /assets/ { + access_log off; + add_header Cache-Control "public, max-age=31536000, immutable"; + try_files $uri =404; + } + + # PWA control surfaces — never cache, so updates propagate + location = /sw.js { + add_header Cache-Control "no-store"; + try_files $uri =404; + } + location = /registerSW.js { + add_header Cache-Control "no-store"; + try_files $uri =404; + } + location = /manifest.webmanifest { + add_header Cache-Control "no-store"; + types { } default_type application/manifest+json; + try_files $uri =404; + } + + # Workbox precache + sourcemap helpers + location ~* ^/workbox-.*\.js$ { + add_header Cache-Control "public, max-age=31536000, immutable"; + try_files $uri =404; + } + + # SPA fallback — every other path serves index.html (no cache) + location / { + add_header Cache-Control "no-store"; + try_files $uri $uri/ /index.html; + } +}