Deployment Architecture
Deployment Architecture
Project:
{{PROJECT_NAME}}Drop Version:{{VERSION}}0.1.0 Date:{{DATE}}2026-02-23 Author:{{AUTHOR}}Platform Architect (AI) Status:Draft |In Review| ApprovedReviewers:{{REVIEWERS}}Alem Bašić (CEO)
Document History
| Version | Date | Author | Changes |
|---|---|---|---|
| 0.1 | Initial draft from source code analysis |
1. Overview
Drop is a remittance and QR payment application deployed on AWS in the eu-west-1 (Ireland) region. The application uses a Next.js 16 standalone server containerised in a multi-stage Docker image and served via AWS App Runner. The database is RDS PostgreSQL (db.t4g.micro) with automated daily snapshots and 7-day retention. All infrastructure is production-grade; staging runs on Fly.io (Stockholm).
System: {{PROJECT_NAME}}Drop — Remittance + QR Payment App
Cloud Provider: {{CLOUD_PROVIDER}}AWS (production), Fly.io (staging)
Provider Rationale: {{RATIONALE}}AWS App Runner chosen for fully managed container hosting with zero infrastructure management; auto-scaling built-in; GDPR data residency satisfied by eu-west-1 (Ireland).
Architecture Pattern: {{PATTERN}}Monolith — single Next.js app serving both frontend and API routes.
2. Infrastructure Topology
graph TB
subgraph Internet
USER[End Users]
CDN[CDNUsers / CloudFront]Mobile App]
BSTACK[BetterStack Uptime Monitor]
end
subgraph PublicAWS Subneteu-west-1
ALB[Applicationsubgraph LoadApp Balancer]Runner
BASTION[BastionAR[AWS Host]App Runner\ndrop-web\neu-west-1]
end
subgraph PrivateECR
SubnetREPO[ECR - App
APP1[App Server 1]
APP2[App Server 2]Repository\n324480209768.dkr.ecr.eu-west-1\n.amazonaws.com/drop-web]
end
subgraph PrivateRDS
SubnetDB[(RDS -PostgreSQL Data
DB_PRIMARY[(Primary DB)16\ndrop-db\ndb.t4g.micro\ndrop-db.czu2qe4quy4v\n.eu-west-1.rds.amazonaws.com)]
DB_REPLICA[(Read Replica)]
CACHE[Redis Cache]
end
subgraph Isolated Subnet
SECRETS[Secrets
Manager]SM[AWS BACKUP[BackupSecrets Storage]Manager\nJWT_SECRET\nDATABASE_URL\nSLACK_WEBHOOK_URL]
end
subgraph CloudWatch
LOGS[CloudWatch Logs\n/aws/apprunner/drop-web/...]
end
end
subgraph Staging - Fly.io Stockholm
FLY[Fly.io App\ndrop-staging\nSQLite ephemeral]
end
subgraph External Services
SUMSUB[Sumsub KYC API\napi.sumsub.com]
BANKID[BankID OIDC\nNorwegian eID]
SLACK[Slack Webhook\nalai-talk.slack.com]
end
USER --> CDNAR
CDNBSTACK --> ALBAR
ALBAR --> APP1DB
ALBAR --> APP2SM
APP1AR --> DB_PRIMARYLOGS
APP2AR --> DB_PRIMARYSUMSUB
APP1AR --> CACHEBANKID
DB_PRIMARYAR --> DB_REPLICASLACK
APP1REPO --> SECRETSAR
3. Networking Architecture
3.1 VPC / VNET Design
App Runner is a fully managed service — AWS manages the underlying VPC. Drop's RDS instance is in the default VPC in eu-west-1. No custom CIDR ranges are managed by Drop at this stage.
| Network | CIDR | Purpose |
|---|---|---|
| AWS Default VPC |
||
Note: Custom VPC with private subnets is tracked as a security hardening item for v1.0 production launch.
3.2 Load Balancer Configuration
App Runner provides a built-in HTTPS load balancer — no ALB/NLB managed by Drop.
| Parameter | Value |
|---|---|
| Type | |
| Protocol | HTTPS (TLS 1.2+) — App Runner enforced |
| SSL Termination | At |
| Health Check Path | /api/health |
| Health Check Interval | |
| Unhealthy Threshold | |
3.3 DNS Architecture
| Record | Type | Value | TTL |
|---|---|---|---|
9ef3szvvsb.eu-west-1.awsapprunner.com |
|||
DNS Provider: {{DNS_PROVIDER}}TBD — requires domain transfer from getdrop.no registration (drop.no owned by TV2).
Failover Strategy: {{FAILOVER_STRATEGY}}Manual failover via DNS update to secondary App Runner service in eu-north-1.
3.4 CDN Configuration
| Parameter | Value |
|---|---|
| Provider | |
.next/static/ | via |
| WAF Integration |
Note: CDN and WAF integration planned for v1.0 launch via CloudFront + AWS WAF.
4. Compute
4.1 Container Orchestration
Platform: {{ORCHESTRATION}}AWS App Runner (managed container service — no cluster management)
| Component | Configuration | Notes |
|---|---|---|
arn:aws:apprunner:eu-west-1:324480209768:service/drop-web/8e45b0d335304487a1880f4e32d6aeec |
||
| Production | ||
| Container Registry | 324480209768.dkr.ecr.eu-west-1.amazonaws.com/drop-web |
|
| Service URL | https://9ef3szvvsb.eu-west-1.awsapprunner.com |
Production |
| Base Image | node:22-alpine |
Multi-stage build |
| Non-root user | nextjs (UID 1001) |
Security hardened |
| Exposed Port | 3000 | |
| Entrypoint | node server.js |
Next.js standalone |
4.2 Serverless Functions
No
4.3 Instance Sizing & Auto-Scaling
| Service | Min | Max | Scale Trigger | |
|---|---|---|---|---|
| drop-staging | Fly.io | 0 | Auto | Scales to zero when idle, auto-start on request |
Scale-OutApp Policy:Runner Auto-Scaling: {{SCALE_OUT}}Managed instance Scale-Inminimum Policy:in {{SCALE_IN}}production, scale-to-zero Scale-Inin Cooldown:staging {{COOLDOWN}}min(Fly.io).
5. Storage
5.1 Database Hosting
| Database | Engine | Version | Hosting | Instance | Storage | HA |
|---|---|---|---|---|---|---|
| drop-staging (staging) | SQLite (better-sqlite3 12.6.2) | — | Fly.io ephemeral volume | — | — | No HA |
Connection Pooling: {{POOL_TOOL}}Application-level (no PgBouncer currently)
Max Connections: {{MAX_CONN}}Default RDS db.t4g.micro (~85 connections)
Connection String: Stored in {{SECRET_LOCATION}}AWS Secrets Manager (DATABASE_URL); never hardcoded)hardcoded.
DB Endpoint: drop-db.czu2qe4quy4v.eu-west-1.rds.amazonaws.com:5432
DB Name: dropapp
DB User: dropuser
5.2 Object Storage
No
5.3 File Storage
| Storage | Type | Mount Point | Purpose | Size |
|---|---|---|---|---|
/app/data |
6. Security
6.1 Network Security Groups / Firewall Rules
| Security Group | Direction | Port | Protocol | Source / Destination | Purpose |
|---|---|---|---|---|---|
| Inbound | 443 | TCP | 0.0.0.0/0 | HTTPS from internet | |
| Outbound | TCP | ||||
| TCP |
6.2 WAF Configuration
WAF Provider: {{WAF_PROVIDER}}None currently (planned for v1.0 via CloudFront + AWS WAF)
Planned rules:
| Rule Group | Purpose | Action |
|---|---|---|
| AWSManagedRulesCommonRuleSet | OWASP Top 10 | Block |
| AWSManagedRulesSQLiRuleSet | SQL injection | Block |
| Rate limiting | Count → Block |
Current mitigation: Application-level rate limiting in middleware.ts using rate_limits DB table (10 req/min on auth endpoints, 120 req/min on public rate endpoints).
6.3 Secrets Management
Secret Store: {{SECRET_STORE}}AWS Secrets Manager (auto-detected via AWS_SECRET_ARN env var) with fallback to environment variables.
| Secret | Rotation Schedule | Access |
|---|---|---|
DATABASE_URL |
90 days | App Runner role only |
JWT_SECRET |
90 |
App Runner role only |
SLACK_WEBHOOK_URL |
On compromise | App Runner role only |
BANKID_CLIENT_SECRET |
Per BankID policy | App Runner role only |
SUMSUB_API_KEY |
180 days | App Runner role only |
| TLS certificates | ||
Caching: Secrets cached in-memory for 5 minutes (configurable TTL via initSecrets()).
6.4 IAM Roles & Policies
| Role | Trusted By | Key Permissions | Purpose | ||
|---|---|---|---|---|---|
secretsmanager:GetSecretValue, ecr:GetAuthorizationToken |
Application runtime | ||||
| GitHub Actions (OIDC) | apprunner:StartDeployment, ecr:PushImage, ecr:BatchCheckLayerAvailability |
CI/CD | |||
arn:aws:iam::324480209768:role/rds-monitoring-role |
rds:DescribeDBInstances, |
7. Cost Estimation
| Component | Service | Spec | Est. Monthly Cost |
|---|---|---|---|
| Compute | ~$ |
||
| Database | ~$ |
||
| 500MB free, then $ | |||
| ~$ |
|||
| Monitoring | ~$2–5 | ||
| BetterStack | Uptime monitoring | Free tier (3 monitors) | $ |
| Total | ~$ |
Cost Optimization Notes:
- App Runner scales to minimum instances in off-peak; staging (Fly.io) scales to zero.
- db.t4g.micro is ARM-based Graviton — 20% cheaper than equivalent x86 instance.
- Reserved instance / savings plan upgrade planned at v1.0 launch.
8. High Availability Design
| Component | HA Strategy | Failover Time | Notes | ||
|---|---|---|---|---|---|
| Application (App Runner) | Immediate ( |
App Runner restarts unhealthy instances automatically | |||
| Database (RDS) | Single-AZ with automated backups | 30 min (snapshot restore) | Multi-AZ | for ||
RTO Target: {{RTO}}10 minutes (App Runner restart) / 30 minutes (RDS restore)
RPO Target: {{RPO}}5 minutes (RDS PITR) / 24 hours (daily snapshot)
9. Multi-Region Considerations
Current: {{REGION_STRATEGY}}Single-region (eu-west-1 Ireland)
Primary Region: {{PRIMARY_REGION}}eu-west-1 (Ireland)
Secondary Region: {{SECONDARY_REGION}}eu-north-1 (Stockholm) — manual failover target only
Rationale: {{MULTI_REGION_RATIONALE}}Single-region deployment appropriate for MVP/pre-production phase. Norwegian users served well from eu-west-1. GDPR data residency satisfied within EU. Cost-benefit favours backup-based recovery over active-active replication at current scale.
Data Replication: {{REPLICATION_STRATEGY}}None (manual snapshot copy to eu-north-1 on region failover)
Failover Procedure: See disaster-recovery-plan.md
10. Related Documents
- CI/CD Pipeline
- Environment Configuration
- Infrastructure as Code
- Monitoring & Observability
- Disaster Recovery Plan
Approval
| Role | Name | Date | Signature |
|---|---|---|---|
| Author | Platform Architect (AI) | 2026-02-23 | |
| Reviewer | |||
| Approver | Alem Bašić |