Deployment Plan
AWS infrastructure and deployment strategy
Deployment Plan
Current AWS infrastructure
The application is deployed to AWS eu-central-1 using OpenTofu (Terraform-compatible) and a small set of infrastructure stacks under infra/.
1. Compute & containers – ECS Fargate
- ECR repository:
info-ppaai-app-prod-webfor the production image. - ECS Fargate service running the Next.js app (Bun runtime), one task definition family per environment.
- Application Load Balancer (ALB) in front of the service:
- HTTP (80) listener redirects to HTTPS (443).
- HTTPS listener terminates TLS using an ACM certificate for
ppaai.info.nland*.ppaai.info.nl.
- The task definition injects:
DATABASE_URLfrom AWS Secrets Manager.BETTER_AUTH_SECRETfrom AWS Secrets Manager.CORS_ORIGIN/BETTER_AUTH_URLpointing athttps://ppaai.info.nl.
2. Networking – VPC & security groups
- Single VPC with CIDR
10.0.0.0/16. - Two public subnets used by the ALB, ECS tasks, and the Aurora cluster subnet group.
- Security groups:
- ALB SG: allows HTTP/HTTPS from the internet.
- ECS SG: allows traffic from ALB SG on port 3000; outbound to the internet.
- DB SG: allows Postgres (5432) from ECS SG only.
3. Database – Aurora PostgreSQL Serverless v2
- Aurora PostgreSQL Serverless v2 cluster for the application database.
- Serverless capacity between 0.5 and 2 ACUs.
- Encrypted at rest, in the VPC, with 7-day backup retention and
skip_final_snapshot = truefor easier teardown in non-prod. - Drizzle ORM migrations run against this database from CI/local.
4. DNS & TLS – Route 53, ACM, custom domain
- Public Route 53 hosted zone for
ppaai.info.nlmanaged byinfra/dns. - Parent zone
info.nlis hosted in PowerDNS; NS records delegateppaai.info.nlto Route 53 using theppaai_name_serversoutput. infra/dns_acm.tf:- Requests an ACM certificate for
ppaai.info.nland*.ppaai.info.nl. - Uses DNS validation in the
ppaai.info.nlhosted zone. - Creates an A/ALIAS record pointing
ppaai.info.nlat the ALB.
- Requests an ACM certificate for
- The result is HTTPS-only access to the app at
https://ppaai.info.nl/with automatic HTTP→HTTPS redirect.
5. Email – Amazon SES for ppaai.info.nl
infra/emailmanages SES for the subdomain:- SES domain identity for
ppaai.info.nl. - TXT record for SES verification.
- DKIM enabled with three CNAME records in Route 53.
- SES domain identity for
- ECS task IAM role has an inline policy allowing
ses:SendEmailandses:SendRawEmailso the app can call SES via the AWS SDK without SMTP credentials. - The web app uses a small helper (
apps/web/src/lib/email.ts) and an API route (POST /api/test-email) to send emails fromno-reply@ppaai.info.nl. - While the SES account remains in the sandbox, recipient addresses must also be verified or SES will reject the message. For production, SES should be moved out of sandbox.
6. Secrets management – AWS Secrets Manager
infra/secrets.tfmanages secrets used by the app:db_url: full Postgres connection string built from the Aurora cluster endpoint, database name, and generated password.better_auth_secret: random 64-character secret for Better Auth.
- ECS task definition references these secrets by ARN so they are injected at runtime.
7. State backend – S3 & DynamoDB
infra/bootstrapcreates the shared OpenTofu backend resources:- S3 bucket:
info-ppaai-app-tfstatefor state files. - DynamoDB table:
info-ppaai-app-tfstate-locksfor state locking.
- S3 bucket:
- Main infra (
infra/), DNS (infra/dns), and email (infra/email) stacks all use this backend, configured viatofu init -backend-config=...in the Makefile.
CI/CD pipeline (GitHub Actions)
Deploy workflow
The workflow .github/workflows/deploy.yml handles production deployments:
- Triggers:
pull_requestclosed intomain(and merged) for relevant paths.workflow_dispatchfor manual runs.- Skips deploy if the PR has
no-code-changeorskip-deploylabels.
- Steps:
- Checkout repository.
- Configure AWS credentials.
- Install Bun and dependencies.
- Build the app (
bun run build). - Build and push a Docker image (linux/amd64) to ECR (
info-ppaai-app-prod-web). - Run OpenTofu in
infra/to apply infrastructure changes withTF_VAR_app_imageset to the pushed image URI. - Output the ALB DNS name at the end.
This results in a new ECS task definition revision pointing at the fresh image, and the service is rolled forward via the ALB.
Environment management
- Local development:
- Postgres via Docker Compose.
- Drizzle migrations via
bun db:generate/bun db:migrate. - App via
bun run dev.
- Production:
- Single
prodenvironment in AWS, controlled by the deploy workflow and the Makefile. - Domain
ppaai.info.nlfronted by ALB with HTTPS.
- Single
Future work may introduce separate staging/pre-production environments that reuse the same modules with different project_name / environment prefixes.
Backup & disaster recovery
- Automated Aurora snapshots with 7-day retention and point-in-time recovery.
- All infrastructure is defined as code (OpenTofu), allowing rapid rebuilds in another region/account if needed.
Transactional emails
Transactional emails (verification, password reset, organisation invites) are implemented as a thin layer on top of SES:
- Templates:
@info-ppaai-app/transactionalwith React Email components inpackages/transactional/src/emails(for example, theVerificationRequestemail). - App helper:
apps/web/src/lib/auth-emails.ts, which renders templates and calls the SES transport. - SES transport:
apps/web/src/lib/email.ts, which wraps@aws-sdk/client-sesand uses either local AWS credentials or the ECS task role in production.
To smoke-test the full pipeline from the app in development:
bun run dev
curl -X POST http://localhost:3000/api/test-verification-email \
-H 'Content-Type: application/json' \
-d '{"to":"you@yourdomain.com"}'This route uses the same helpers that will be wired into Better Auth’s verification and password-reset flows, so those emails share the same templates and SES configuration.
Security & cost considerations
- HTTPS-only via ALB + ACM; HTTP is redirected to HTTPS.
- VPC security groups restrict DB access to ECS tasks only.
- IAM roles are scoped to least privilege for ECS execution and task roles.
- Secrets are stored in AWS Secrets Manager and injected at runtime.
- Aurora Serverless v2 and ECS tasks can be scaled down when idle to reduce cost.
- Future improvements may include:
- CloudWatch log retention policies per log group.
- ECR lifecycle policies for old images.
- Fargate Spot for non-production environments.