Overview
Open Wearables publishes two production images to Docker Hub:| Image | Service | Port |
|---|---|---|
themomentum/open-wearables-backend | FastAPI API + Celery worker/beat/flower | 8000 |
themomentum/open-wearables-frontend | React frontend (TanStack Start, served by Nitro) | 3000 |
scripts/start/app.sh, worker.sh, beat.sh, flower.sh), exactly as in docker-compose.yml.
Configuring the frontend API URL at runtime
What changed: the frontend used to bake
VITE_API_URL into the JavaScript bundle at build time, which meant a prebuilt image could only ever talk to one backend. The API URL is now resolved at runtime, so the same published image works against any backend without rebuilding.VITE_API_URL as an environment variable on the frontend container. The Nitro server reads it at startup and injects it into the served HTML before the app loads:
VITE_API_URL is not set, it falls back to http://localhost:8000.
VITE_API_URL is a single variable used for both build-time (Vite inlines import.meta.env) and runtime configuration. When the same variable is set both in an .env file and via the container’s environment:, the container environment wins — so runtime always overrides any baked-in value.How it works
Because the frontend is server-rendered (TanStack Start on Nitro), the value travels from the container env to the browser like this:- The Nitro server reads
process.env.VITE_API_URLat request time. - It injects
window.__APP_CONFIG__ = { apiUrl: "..." }into the HTML<head>before the app hydrates. - The API client reads that value. (Resolution order: injected runtime value →
VITE_API_URLbaked at build →http://localhost:8000.)
frontend/src/lib/api/runtime-config.ts.
Example: deploying one client
For a client served atclient1.com with its API at api.client1.com:
config/.env), so its image needs no per-client rebuild either.
Publishing the images
Images are built and pushed manually via the Publish Docker images GitHub Actions workflow (.github/workflows/publish-images.yml):
- Go to Actions → Publish Docker images → Run workflow.
- Both images are built and pushed, tagged with the commit SHA, plus
latestwhen run frommain, plus any optional tag you provide.
DOCKERHUB_USERNAME and DOCKERHUB_TOKEN (a Docker Hub access token with Read & Write scope). No API URL is baked into the frontend image — it is always configured at runtime as described above.
Related Guides
- Deploy to Railway - One-click managed deployment
- Raw Payloads Storage - Backend runtime configuration example

