Production Deployment
This guide covers deploying Klaxon to production on AWS, Azure, GCP, or self-hosted infrastructure.
Production Checklist
Before going live, verify:
- [ ] TLS: HTTPS enforced via load balancer or reverse proxy
- [ ] SMTP configured:
SMTP_HOSTset for magic link + email notifications - [ ] FCM configured:
FCM_SERVER_KEYset for push notifications - [ ] Redis configured:
REDIS_URLset for rate limiting - [ ] Postgres backups: automated via cloud provider or cron
- [ ] CORS restricted:
CORS_ORIGINSset to actual frontend domains (not*) - [ ] Dev login disabled:
DEV_LOGIN_ENABLED=false(default) - [ ] Observability:
OTEL_ENABLED=truewith collector endpoint, log aggregation configured - [ ] Health monitoring: alert on
/readyreturning non-200 - [ ] Secrets management: env vars via cloud secrets manager (not plaintext files)
- [ ] Image pinned: Docker image tag pinned to a specific SHA, not
latest
Architecture Overview
Every deployment needs:
| Component | Purpose | Replicas |
|---|---|---|
| Server | HTTP API, MCP, WebSocket | 2+ (stateless) |
| Worker | Push queue, webhooks, email, sweeps | 1 |
| PostgreSQL | Data store | 1 (managed recommended) |
| Redis | Rate limiting | 1 |
The server and worker run the same Docker image — the worker is started with --worker flag.
Self-Hosted (Docker Compose + Traefik)
For VPS or bare metal. See deploy/docker-compose.prod.yml.
# Set your domain and email
export DOMAIN=klaxon.sh
export ACME_EMAIL=admin@klaxon.sh
# Start
docker compose -f deploy/docker-compose.prod.yml up -dTraefik handles TLS via Let's Encrypt automatically. Postgres data persists in a named volume. A daily backup cron dumps to /backups.
AWS (ECS Fargate + RDS + ElastiCache)
Terraform module: deploy/terraform/aws/
cd deploy/terraform/aws
cp terraform.tfvars.example terraform.tfvars
# Edit terraform.tfvars with your values
terraform init
terraform plan
terraform applyResources provisioned:
- ECS Fargate cluster (server: 2 tasks, worker: 1 task)
- RDS PostgreSQL 16 (db.t3.micro, configurable)
- ElastiCache Redis (cache.t3.micro)
- Application Load Balancer with ACM TLS certificate
- ECR repository for Docker images
- CloudWatch log group
- VPC with public/private subnets
Pulumi alternative: deploy/pulumi/aws/
Azure (Container Apps + Flexible Server + Cache)
Terraform module: deploy/terraform/azure/
cd deploy/terraform/azure
cp terraform.tfvars.example terraform.tfvars
terraform init && terraform applyResources provisioned:
- Azure Container Apps (server + worker)
- Azure Database for PostgreSQL Flexible Server
- Azure Cache for Redis
- Azure Container Registry
- Application Gateway with managed TLS
Pulumi alternative: deploy/pulumi/azure/
GCP (Cloud Run + Cloud SQL + Memorystore)
Terraform module: deploy/terraform/gcp/
cd deploy/terraform/gcp
cp terraform.tfvars.example terraform.tfvars
terraform init && terraform applyResources provisioned:
- Cloud Run services (server + worker)
- Cloud SQL PostgreSQL 16
- Memorystore Redis
- Artifact Registry for Docker images
- Cloud Load Balancing with managed SSL certificate
Pulumi alternative: deploy/pulumi/gcp/
Kubernetes (Helm)
See the Kubernetes / Helm guide. The Helm chart is published to oci://ghcr.io/ottercoders/charts.
helm install klaxon oci://ghcr.io/ottercoders/charts/klaxon \
--set secrets.databaseUrl="postgres://..." \
--set ingress.enabled=true \
--set ingress.hosts[0].host=klaxon.shEnvironment Variables Reference
See Configuration for the full list. Key production variables:
DATABASE_URL=postgres://klaxon:SECRET@db:5432/klaxon
REDIS_URL=redis://cache:6379
CORS_ORIGINS=https://klaxon.sh
APP_URL=https://klaxon.sh
SMTP_HOST=smtp.sendgrid.net
SMTP_USER=apikey
SMTP_PASS=SG.xxx
SMTP_FROM=noreply@klaxon.sh
FCM_SERVER_KEY=AAAA...
OTEL_ENABLED=true
OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4317