Info PPAAI App

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-web for 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.nl and *.ppaai.info.nl.
  • The task definition injects:
    • DATABASE_URL from AWS Secrets Manager.
    • BETTER_AUTH_SECRET from AWS Secrets Manager.
    • CORS_ORIGIN / BETTER_AUTH_URL pointing at https://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 = true for 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.nl managed by infra/dns.
  • Parent zone info.nl is hosted in PowerDNS; NS records delegate ppaai.info.nl to Route 53 using the ppaai_name_servers output.
  • infra/dns_acm.tf:
    • Requests an ACM certificate for ppaai.info.nl and *.ppaai.info.nl.
    • Uses DNS validation in the ppaai.info.nl hosted zone.
    • Creates an A/ALIAS record pointing ppaai.info.nl at the ALB.
  • 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/email manages 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.
  • ECS task IAM role has an inline policy allowing ses:SendEmail and ses:SendRawEmail so 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 from no-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.tf manages 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/bootstrap creates the shared OpenTofu backend resources:
    • S3 bucket: info-ppaai-app-tfstate for state files.
    • DynamoDB table: info-ppaai-app-tfstate-locks for state locking.
  • Main infra (infra/), DNS (infra/dns), and email (infra/email) stacks all use this backend, configured via tofu init -backend-config=... in the Makefile.

CI/CD pipeline (GitHub Actions)

Deploy workflow

The workflow .github/workflows/deploy.yml handles production deployments:

  • Triggers:
    • pull_request closed into main (and merged) for relevant paths.
    • workflow_dispatch for manual runs.
    • Skips deploy if the PR has no-code-change or skip-deploy labels.
  • Steps:
    1. Checkout repository.
    2. Configure AWS credentials.
    3. Install Bun and dependencies.
    4. Build the app (bun run build).
    5. Build and push a Docker image (linux/amd64) to ECR (info-ppaai-app-prod-web).
    6. Run OpenTofu in infra/ to apply infrastructure changes with TF_VAR_app_image set to the pushed image URI.
    7. 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 prod environment in AWS, controlled by the deploy workflow and the Makefile.
    • Domain ppaai.info.nl fronted by ALB with HTTPS.

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/transactional with React Email components in packages/transactional/src/emails (for example, the VerificationRequest email).
  • 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-ses and 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.