# syntax=docker/dockerfile:1.7 # Build stage — produces the static SPA bundle in /app/build. FROM node:24-alpine AS builder WORKDIR /app COPY package.json package-lock.json ./ RUN --mount=type=cache,target=/root/.npm npm ci COPY . . RUN npm run build # Runtime stage — a minimal Caddy that serves the SPA and falls back to # index.html for client-side routing. The main reverse-proxy Caddy in # the compose stack proxies `/` to this container. FROM caddy:2-alpine AS runtime WORKDIR /srv COPY --from=builder /app/build /srv RUN printf ':80 {\n\troot * /srv\n\ttry_files {path} /index.html\n\tfile_server\n\tencode zstd gzip\n\tlog {\n\t\toutput stdout\n\t\tformat console\n\t}\n}\n' > /etc/caddy/Caddyfile EXPOSE 80 HEALTHCHECK --interval=10s --timeout=2s --retries=3 \ CMD wget -qO- http://127.0.0.1/ >/dev/null 2>&1 || exit 1