Deployment Guide

AXIOM Exchange's API layer is stateless (auth is JWT/API-key, no server-side sessions), so the same container image runs unmodified on a laptop, on-prem behind nginx, or autoscaled behind a cloud API Gateway.

1. Local (docker-compose)

Covered on the Overview page. Two API replicas + nginx + Postgres/PostGIS

2. On-prem / self-managed VM or bare-metal

  1. Provision PostgreSQL 16+ with PostGIS available (CREATE EXTENSION postgis;). Apply api/app/db/schema.sql — it is the canonical DDL and has been verified to apply clean from scratch.
  2. Build and push the API image: docker build -t axiom-exchange-api ./api.
  3. Run N containers of that image — each is stateless and identical.
  4. Point nginx's upstream axiom_api { ... } block at all N instances.
  5. Terminate TLS at nginx (add a listen 443 ssl block) or at a proxy in front of it.

3. AWS

Compute: ECS Fargate service (or EKS) behind an Application Load Balancer. nginx can stay in the path as an internal LB layer, or be dropped entirely — the ALB already does health-check-aware balancing across tasks. Keep nginx if you want its rate limiting, gzip, and caching.

Database: Amazon Aurora PostgreSQL-Compatible with CREATE EXTENSION postgis;. Aurora Serverless v2 suits spiky trading-hours load. Read replicas can take the read-heavy /geo/* and /assets traffic (the app does no read/write splitting itself — do it at the connection string or via RDS Proxy with a read-only endpoint).

API Gateway: import /openapi.json into AWS API Gateway (HTTP API, OpenAPI import) and point the integration at the ALB. Use a Cognito or Lambda authorizer, then forward identity to AXIOM via X-Axiom-Gateway-Identity + X-Axiom-Gateway-Secret (set to your AXIOM_JWT_SECRET). For reseller scoping, have the authorizer also set X-Axiom-Reseller-Id from the validated claims — the service then filters every tenant-aware query to that reseller without trusting the client.

Config: AXIOM_DATABASE_URL → the Aurora cluster endpoint, AXIOM_ENVIRONMENT=production, secrets via AWS Secrets Manager or SSM injected as container env vars — never baked into the image.

4. Azure

Compute: Azure Container Apps (built-in autoscaling and revision-based rollout) or AKS. Same nginx-optional consideration as AWS.

Database: Azure Database for PostgreSQL — Flexible Server, with postgis in the azure.extensions allowlist (Portal → Extensions, or az postgres flexible-server parameter set --name azure.extensions --value postgis), then CREATE EXTENSION postgis;.

API Gateway: Azure API Management. Import /openapi.json as an API definition; APIM handles subscription keys/OAuth, quotas, and policies, then forwards to the Container Apps ingress. Use the same X-Axiom-Reseller-Id pattern for tenant scoping.

Config: AXIOM_DATABASE_URL → the Flexible Server endpoint, secrets via Key Vault referenced from Container Apps secret bindings.

5. Kong

See gateway/kong.example.yaml in the repository for a working declarative config: key-auth, rate-limiting, and cors plugins in front of the nginx/API service. Apply with deck sync -s gateway/kong.example.yaml.

6. Scaling checklist

7. Environment variable reference

Variable Default Notes
AXIOM_ENVIRONMENT local local enables schema autocreate on boot. Use staging/production elsewhere.
AXIOM_DATABASE_URL local postgres postgresql+asyncpg://user:pass@host:5432/db
AXIOM_REDIS_URL local redis redis://host:6379/0
AXIOM_JWT_SECRET dev placeholder Change in every non-local environment. Also doubles as the gateway-trust shared secret.
AXIOM_JWT_EXPIRE_MINUTES 60
AXIOM_API_KEYS ["axiom-local-dev-key"] JSON array of valid static keys.
AXIOM_CORS_ORIGINS ["*"] Restrict in production.
AXIOM_DEFAULT_SRID 4326 WGS84 — matches ArcGIS/GeoJSON defaults.