> ## Documentation Index
> Fetch the complete documentation index at: https://openwearables.io/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Docker Images

> Run Open Wearables from the prebuilt Docker Hub images and point the frontend at any backend at runtime

## 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` |

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

<Note>
  **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.
</Note>

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:

```bash theme={null}
docker run -p 3000:3000 \
  -e VITE_API_URL=https://api.client1.com \
  themomentum/open-wearables-frontend:latest
```

The same image with a different value points at a different backend — no rebuild:

```bash theme={null}
docker run -p 3000:3000 \
  -e VITE_API_URL=https://api.client2.com \
  themomentum/open-wearables-frontend:latest
```

If `VITE_API_URL` is not set, it falls back to `http://localhost:8000`.

<Note>
  `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.
</Note>

### 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`:

```yaml theme={null}
services:
  frontend:
    image: themomentum/open-wearables-frontend:latest
    environment:
      - VITE_API_URL=https://api.client1.com
    ports:
      - "3000:3000"

  backend:
    image: themomentum/open-wearables-backend:latest
    command: scripts/start/app.sh
    env_file:
      - ./config/.env
    ports:
      - "8000:8000"
```

The backend reads all of its configuration from the environment at runtime (`config/.env`), so its image needs no per-client rebuild either.

## Related Guides

* [Deploy to Railway](/docs/deployment/railway) - One-click managed deployment
* [Raw Payloads Storage](/docs/dev-guides/raw-payload-storage) - Backend runtime configuration example
