Deployment¶
Development (Docker Compose)¶
The docker-compose.yml in the project root starts the full stack in development mode:
All services use development configurations: hot-reload, debug logging, and development credentials.
Production Builds¶
Production images live alongside their dev counterparts in each app directory. Use docker-compose.prod.yml to build and run the full stack with production settings.
Backend¶
backend/Dockerfile.prod is a multi-stage Python 3.12-slim build. Highlights:
- Stage 1 (builder) — installs build tooling, resolves
pyproject.tomldeps, and optionally downloads the MaxMind GeoLite2 database at build time (viaMAXMIND_ACCOUNT_ID/MAXMIND_LICENSE_KEYbuild args; GeoLite2 can also be fetched at runtime from admin → Settings → Geocoding). - Stage 2 (runtime) — slim image that copies in only
site-packages, app code, and the GeoLite2 DB. - Entrypoint — Uvicorn with
--workerssized for the host.
Warning
Do not use --reload in production. Use --workers to match your CPU count.
Storefront / Admin¶
Both SvelteKit apps ship their own Dockerfile and build with @sveltejs/adapter-node into a build/ directory served by node build. Dev images mount source for hot reload; production builds bake the compiled output in.
Run npx houdini generate && npx svelte-check as part of the build (already wired into npm run check) so type-checking failures block deploys.
Environment Variables¶
Required variables for production:
| Variable | Description |
|---|---|
DATABASE_URL | PostgreSQL connection string (use asyncpg driver) |
REDIS_URL | Redis connection string |
SECRET_KEY | JWT signing secret (generate with python -c "import secrets; print(secrets.token_urlsafe(64))") |
REDPANDA_BROKER | Redpanda/Kafka broker address |
TEMPORAL_HOST | Temporal server address |
MEILISEARCH_URL | Meilisearch endpoint (admin search) |
MEILISEARCH_API_KEY | Meilisearch admin API key |
TYPESENSE_URL | Typesense endpoint (storefront search) |
TYPESENSE_API_KEY | Typesense admin API key |
TYPESENSE_SEARCH_ONLY_KEY | Typesense search-only key (for scoped key generation) |
FILE_STORAGE_DIR | Local filesystem directory for uploads (default: uploads). Used by the built-in local storage strategy. |
PUBLIC_API_BROWSER_ORIGIN | Optional on API: browser-reachable origin (e.g. https://api.example.com). When set, the local file strategy returns absolute /uploads URLs. Set the same value on the admin service so the upload BFF can rewrite relative URLs if the API omits this setting. |
S3_ENDPOINT | S3-compatible storage endpoint (used by ext_minio, ext_s3, ext_digitalocean_spaces) |
S3_PUBLIC_URL | Public URL for S3 objects (browser-accessible). Required when S3_ENDPOINT uses a Docker-internal hostname. Falls back to S3_ENDPOINT if unset. |
S3_ACCESS_KEY | S3 access key (required when using a storage extension) |
S3_SECRET_KEY | S3 secret key (required when using a storage extension) |
S3_BUCKET | S3 bucket name |
Tip
Use a secrets manager (AWS Secrets Manager, Vault, etc.) for SECRET_KEY, database credentials, and API keys. Never commit secrets to the repository.
Database Migrations¶
Run migrations before starting the application:
For first-time setup, also run the seed script:
Health Check¶
The API exposes a { health } GraphQL query that returns "Vectis Commerce API is healthy". Use this for load balancer and container health checks:
curl -X POST http://localhost:8000/graphql \
-H "Content-Type: application/json" \
-d '{"query": "{ health }"}'
Scaling¶
| Service | Scaling Strategy |
|---|---|
| API | Horizontal — add more Uvicorn worker processes or container replicas |
| Storefront / Admin | Horizontal — stateless Node.js processes behind a load balancer |
| Temporal Worker | Horizontal — add worker replicas; Temporal distributes work automatically |
| Event Consumer | Single instance per consumer group (Redpanda handles partitioning) |
| PostgreSQL | Vertical or read replicas |
| Redis | Single instance or Redis Cluster for high-traffic sessions |
TLS / HTTPS¶
Place a reverse proxy (Nginx, Caddy, or cloud load balancer) in front of all services. Terminate TLS at the proxy. Internal service-to-service communication can use HTTP within a private network.