Production deployment
A production Fovea install is eight containers wired together by
the docker-compose.yml at the repo root: Postgres, Redis,
backend, model service, frontend, an OpenTelemetry collector,
Prometheus, and Grafana. The last two are part of the default
observability stack but can be omitted if you forward telemetry
to a remote vendor instead. Everything comes from the same image
set that CI publishes on every tagged release; nothing in
docker-compose.yml is dev-only.
Prerequisites
- A Linux host with at least 16 GB RAM. The default
docker compose upstack runs the CPU model service (usingmodels-cpu.yaml); GPU inference requires the--profile gpuoverride, which activates themodel-service-gpuvariant and selectsmodels.yaml. CPU inference is roughly an order of magnitude slower per video than GPU. - Docker and Docker Compose v2.
- A Postgres-reachable disk volume for persistent data. The
default
docker-compose.ymlmounts a named volume; production installs typically bind a host path so backups are easy. - A strong value for
SESSION_SECRET. The backend uses session-cookie auth and readsSESSION_SECRETat startup (server/src/app.ts); the compose default is a placeholder that must be overridden in production. The initial admin account is seeded fromADMIN_PASSWORDon first boot; subsequent accounts are minted through the registration form whenALLOW_REGISTRATION=true.
First-time setup
- Clone the repo on the host and check out the release tag you
plan to run. Tags follow
vMAJOR.MINOR.PATCH. - Copy
.env.exampleto.envand fill in the required variables listed in Reference / Environment variables. The variables that almost always need editing areSESSION_SECRET,ADMIN_PASSWORD,DATABASE_URL,STORAGE_PATH, and any external API keys (OPENAI_API_KEY,ANTHROPIC_API_KEY,HF_TOKEN). docker compose pullto fetch the image set for this tag.docker compose up -dto start the stack. The backend container's startup command runsprisma migrate deployandnode prisma/seed.cjsbefore starting the server, so migrations and the RBAC permission catalog are applied automatically on first boot (both steps are idempotent).- Mint the first admin account either by setting
ADMIN_PASSWORDin.envbefore step 4 (the seeder reads it and creates the initial admin) or by temporarily settingALLOW_REGISTRATION=trueand registering through the form.
What runs where
| Container | Port (internal) | Purpose |
|---|---|---|
frontend | 3000 | nginx serving the React build |
backend | 3001 | Fastify API + BullMQ queue producers |
model-service | 8000 | FastAPI inference service |
postgres | 5432 | Application database |
redis | 6379 | Queue broker for BullMQ |
otel-collector | 4318 | OTLP HTTP endpoint for traces and metrics |
The frontend container terminates HTTP. In a real deployment you typically front it with a reverse proxy (caddy, nginx, Traefik, an ALB) that holds the TLS certificate and forwards to the frontend container on port 3000. The reverse proxy is the right place to configure rate limits, IP allowlists, and the HSTS / CSP headers Fovea does not set itself.
What to expose
Only the reverse-proxied frontend port should be reachable from the public internet. The backend, model service, and Postgres ports are meant to be private. The OTel collector at 4318 accepts traces from both the backend and the model service; if you forward those to a remote vendor, do it through a private network, not the public internet.
Where data lives
- Postgres holds every annotation, persona, ontology, world object, claim, summary, and user record. Lose it and you have lost the install's state.
- The
STORAGE_PATHvolume holds uploaded videos. Losing it loses the videos but leaves all annotation metadata intact; annotations carry stable video IDs. - The
model-cachenamed volume (mounted at/modelsviaTRANSFORMERS_CACHE) holds downloaded model weights. Losing it costs one re-download per model at next use. - Redis holds in-flight job state. Losing it cancels any currently-running detection or summarization job; the user retries from the UI.
What to plan for
- Backup and restore for Postgres and the storage volume.
- Monitoring for the OTel and Prometheus endpoints the stack already emits to.
- Upgrades for the path between releases.
- Troubleshooting for the failure modes operators have seen in the wild.