Skip to main content

Overview

Open Wearables publishes two production images to Docker Hub: The single backend image runs the API, worker, beat, and flower — the command is supplied by your orchestrator (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.
Set 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:
The same image with a different value points at a different backend — no rebuild:
If 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:
  1. The Nitro server reads process.env.VITE_API_URL at request time.
  2. It injects window.__APP_CONFIG__ = { apiUrl: "..." } into the HTML <head> before the app hydrates.
  3. The API client reads that value. (Resolution order: injected runtime value → VITE_API_URL baked at build → http://localhost:8000.)
The logic lives in frontend/src/lib/api/runtime-config.ts.

Example: deploying one client

For a client served at client1.com with its API at api.client1.com:
The backend reads all of its configuration from the environment at runtime (config/.env), so its image needs no per-client rebuild either.