Deploy on AWS
AWS offers two useful Doso shapes:
- App Runner for a smaller operational surface.
- ECS Fargate for private service discovery, controlled scaling, durable workers, and one Application Load Balancer.
Both use RDS or Aurora PostgreSQL.
Architecture#
Choose ECS for production SaaS or when the API must stay at one controlled replica. App Runner is the simpler single-service option.
Prerequisites#
- VPC with private subnets across at least two Availability Zones.
- RDS/Aurora Postgres 16 with automated backups and encryption.
- ECR repositories for API and web images.
- Secrets Manager entries for database URLs and encryption/auth keys.
- ACM certificate and Route 53 or external DNS for
app.example.com.
Database#
Keep RDS private. Allow port 5432 only from the web/API task security groups. Create separate application and auth databases:
1DATABASE_URL=postgresql://doso:PASSWORD@DB_HOST:5432/doso?sslmode=require2BETTER_AUTH_DATABASE_URL=postgresql://doso:PASSWORD@DB_HOST:5432/doso_auth?sslmode=requireUse RDS Proxy when autoscaling many short-lived services; otherwise keep SQLx pool sizes small and bounded.
Build and push#
1AWS_REGION=us-east-12ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)3API_IMAGE=$ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/doso-api4WEB_IMAGE=$ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/doso-web5 6aws ecr get-login-password --region "$AWS_REGION" |7 docker login --username AWS --password-stdin \8 "$ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com"9 10docker build -f crates/doso-api/Dockerfile -t "$API_IMAGE:latest" .11docker build -f web/Dockerfile \12 --build-arg NEXT_PUBLIC_DOSO_AUTH_MODE=session \13 --build-arg NEXT_PUBLIC_API_URL= \14 -t "$WEB_IMAGE:latest" web15 16docker push "$API_IMAGE:latest"17docker push "$WEB_IMAGE:latest"Use immutable commit-SHA tags in production.
App Runner#
Create an API service from ECR:
- port
8080; - VPC connector to RDS;
- private service when available, otherwise public HTTPS protected by JWT;
DATABASE_URL,DOSO_SETTINGS_KEY_HEX, and auth variables from Secrets Manager;- health check
/healthz.
Create web from its ECR image:
- port
3000; - custom domain
app.example.com; DOSO_API_PROXY_TARGETset to the API service URL;- Better Auth database and secret from Secrets Manager.
App Runner storage is ephemeral. Use secret-backed key material and Postgres; do not use local SQLite or Git vault persistence.
ECS Fargate#
Create separate task definitions and services for web and api:
- Cloud Map service discovery gives web a private API hostname.
- Only web receives traffic from the public ALB.
- API desired count starts at one.
- Deployment circuit breaker and automatic rollback are enabled.
- Tasks use execution/task roles with least-privilege Secrets Manager access.
- CloudWatch Logs receive stdout/stderr.
Route the ALB's default host to web. Keep /v1/** inside the Next.js proxy so
the API target remains private. Use ECS Exec for controlled diagnostics rather
than exposing admin ports.
Migrations and deployment#
- Run migrations as a one-off Fargate task using the direct database endpoint.
- Deploy API, wait for the health check, then deploy web.
- For ECS, use rolling or CodeDeploy blue/green deployment.
- Verify JWKS, authenticated API calls, SSE, and settings decryption.
- Exercise rollback and restore an RDS snapshot in a non-production account.
Keep the API at one replica until startup recovery, background jobs, live settings, and events are safe across processes.
SaaS#
Skip this. Multi-org is ../../saas.
Operations checklist#
- RDS is private, encrypted, backed up, and has deletion protection.
- Secrets never appear in task definitions or image layers.
- ALB/App Runner HTTPS redirects and security headers are enabled.
- CloudWatch alarms cover 5xx, task restarts, CPU/memory, and database connections.
- ECR image scanning and lifecycle rules are enabled.
Official references: App Runner, ECS Fargate, and RDS PostgreSQL.