AWS / GCP / Azure
The auth backend is a stateless HTTP service. Any container hosting service runs it.
AWS Fargate
Dockerfile is included in the repo. Push to ECR:
aws ecr create-repository --repository-name auth-backend
docker build -t auth-backend .
docker tag auth-backend:latest <acct>.dkr.ecr.<region>.amazonaws.com/auth-backend:latest
docker push <acct>.dkr.ecr.<region>.amazonaws.com/auth-backend:latest
Task definition essentials:
{
"family": "auth-backend",
"networkMode": "awsvpc",
"containerDefinitions": [{
"name": "auth",
"image": "<acct>.dkr.ecr.<region>.amazonaws.com/auth-backend:latest",
"portMappings": [{ "containerPort": 4000 }],
"secrets": [
{ "name": "IDENTSPHERE_JWT_SECRET", "valueFrom": "arn:aws:secretsmanager:..." },
{ "name": "DATABASE_URL", "valueFrom": "arn:aws:secretsmanager:..." }
],
"environment": [
{ "name": "IDENTSPHERE_PUBLIC_BASE_URL", "value": "https://auth.example.com" },
{ "name": "IDENTSPHERE_COOKIES_SECURE", "value": "true" }
],
"healthCheck": {
"command": ["CMD-SHELL", "curl -f http://localhost:4000/health || exit 1"],
"interval": 30,
"timeout": 5,
"retries": 3
}
}]
}
Front with an ALB; terminate TLS at the ALB. RDS Postgres for the DB.
GCP Cloud Run
gcloud run deploy auth-backend \
--image gcr.io/<project>/auth-backend:latest \
--port 4000 \
--region us-central1 \
--allow-unauthenticated \
--set-env-vars IDENTSPHERE_PUBLIC_BASE_URL=https://auth.example.com,IDENTSPHERE_COOKIES_SECURE=true \
--set-secrets IDENTSPHERE_JWT_SECRET=jwt-secret:latest,DATABASE_URL=db-url:latest \
--min-instances 1
--min-instances 1 avoids cold-start latency on first request. Cloud SQL
Postgres for the DB.
Azure Container Apps
az containerapp create \
--name auth-backend \
--resource-group auth \
--image <registry>.azurecr.io/auth-backend:latest \
--target-port 4000 \
--ingress external \
--secrets jwt-secret=$(openssl rand -hex 32) db-url="..." \
--env-vars IDENTSPHERE_PUBLIC_BASE_URL=https://auth.example.com IDENTSPHERE_COOKIES_SECURE=true \
IDENTSPHERE_JWT_SECRET=secretref:jwt-secret DATABASE_URL=secretref:db-url
Azure Database for PostgreSQL Flexible Server for the DB.
Fly.io
fly.toml:
app = "auth-backend"
primary_region = "ord"
[build]
dockerfile = "Dockerfile"
[http_service]
internal_port = 4000
force_https = true
auto_stop_machines = false
min_machines_running = 1
[env]
IDENTSPHERE_PUBLIC_BASE_URL = "https://auth-backend.fly.dev"
IDENTSPHERE_COOKIES_SECURE = "true"
Set secrets:
fly secrets set IDENTSPHERE_JWT_SECRET=$(openssl rand -hex 32)
fly secrets set DATABASE_URL="postgres://..."
fly deploy
Fly Postgres or any external Postgres.
Railway
Add a Postgres plugin, then deploy from GitHub:
railway up
railway variables set IDENTSPHERE_JWT_SECRET=$(openssl rand -hex 32)
railway variables set IDENTSPHERE_COOKIES_SECURE=true
railway variables set IDENTSPHERE_PUBLIC_BASE_URL=https://your-app.up.railway.app
DATABASE_URL is auto-injected from the Postgres plugin.
Render
render.yaml:
services:
- type: web
name: auth-backend
runtime: docker
plan: starter
healthCheckPath: /health
envVars:
- key: IDENTSPHERE_PUBLIC_BASE_URL
value: https://auth-backend.onrender.com
- key: IDENTSPHERE_COOKIES_SECURE
value: 'true'
- key: IDENTSPHERE_JWT_SECRET
generateValue: true
- fromDatabase:
name: auth-db
property: connectionString
key: DATABASE_URL
databases:
- name: auth-db
plan: starter
postgresMajorVersion: 16
Sizing
| Tier | vCPU | Memory | Postgres | Concurrent users |
|---|---|---|---|---|
| Dev | 0.25 | 256 MiB | Shared | ~50 |
| Small | 1 | 1 GiB | 1 vCPU / 4 GiB | ~5,000 |
| Medium | 2 | 4 GiB | 2 vCPU / 8 GiB | ~50,000 |
| Large | 4 | 8 GiB | 4 vCPU / 16 GiB + replicas | ~500,000 |
Argon2id is CPU-hungry on login; size your auth backend for password-hash throughput rather than QPS.
Next steps
- Production checklist — pre-launch audit.
- Monitoring — what to alert on.