Building & Serving
The frontend ships as a static build served by nginx. This page covers building it and how the production image is assembled.
Local build
Section titled “Local build”make frontend-buildThis runs the production build (TypeScript type-check followed by vite build),
emitting static assets to frontend/dist/. For day-to-day work use
make frontend-run-dev instead — see
Frontend Overview.
The Docker image
Section titled “The Docker image”frontend/Dockerfile
is multi-stage:
- Build stage (
node:22-alpine). Runsnpm ci, thennpm run build. TheVITE_*values are passed as build args and inlined into the bundle here. They default to deployment-neutral values (relative API base, integrations disabled). - Runtime stage (
nginx:1.27-alpine). Copies the builtdist/into/usr/share/nginx/htmland the nginx template into/etc/nginx/templates/. Exposes port 80.
Because @vibexp/api-client and @vibexp/design-system resolve from public
npm, the build needs no monorepo context and no registry auth token.
nginx, BACKEND_ORIGIN, and the API proxy
Section titled “nginx, BACKEND_ORIGIN, and the API proxy”The runtime stage sets a single runtime variable:
ENV BACKEND_ORIGIN=http://backend:8080The nginx image renders
nginx.conf.template
with envsubst at container start, substituting only declared variables
(BACKEND_ORIGIN) so nginx’s own runtime variables ($uri, $host,
$upstream, …) are preserved.
The template does two things:
# Reverse-proxy /api/ to the backend.location /api/ { resolver 127.0.0.11 ipv6=off valid=10s; set $upstream "${BACKEND_ORIGIN}"; proxy_pass $upstream$request_uri; # ...forwarded headers...}
# SPA fallback: serve assets, fall back to index.html for client routes.location / { try_files $uri $uri/ /index.html;}The try_files … /index.html fallback is what makes deep links to client-side
routes work — any path that is not a real file is served the SPA shell.
Sentry source maps
Section titled “Sentry source maps”When SENTRY_ORG, SENTRY_PROJECT, and SENTRY_AUTH_TOKEN are all set at build
time (typically in CI), the Sentry build plugin uploads hidden source maps to
Sentry and then deletes the .map files from the output so they are never
deployed publicly. With those variables unset, the build works normally and no
maps are uploaded. See
Frontend Configuration.
Published image & release flow
Section titled “Published image & release flow”The built image is published to GitHub Container Registry as
ghcr.io/vibexp/frontend. Releases are cut via prefixed Git tags: a
frontend-vX.Y.Z GitHub Release builds and pushes
ghcr.io/vibexp/frontend:X.Y.Z (and :latest). The backend is released the same
way via backend-vX.Y.Z tags.
The root docker-compose.yml pulls these published images — see
Self-Hosting and
Docker & Compose.