Skip to content

Deployment

Development (Docker Compose)

The docker-compose.yml in the project root starts the full stack in development mode:

docker compose up -d

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.toml deps, and optionally downloads the MaxMind GeoLite2 database at build time (via MAXMIND_ACCOUNT_ID / MAXMIND_LICENSE_KEY build 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 --workers sized 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:

cd backend && alembic upgrade head

For first-time setup, also run the seed script:

python -m vectis.core.seed

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.