Skip to main content

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 | Approved Reviewers: {{REVIEWERS}}Alem Bašić (CEO)

Document History

Version Date Author Changes
0.1 {{DATE}}2026-02-23 {{AUTHOR}}Platform Architect (AI) 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.

Sumsub,BankID
Network CIDR Purpose
AWS Default VPC / VNET(eu-west-1) {{CIDR_VPC}}AWS-managed MainApp networkRunner boundary+ RDS default networking
PublicApp SubnetRunner AOutbound {{CIDR_PUB_A}}AWS-managed NAT LoadEgress balancers,to NATRDS, gateways
Public Subnet B{{CIDR_PUB_B}}Load balancers, NAT gateways (AZ-B)
Private Subnet A{{CIDR_PRIV_A}}Application servers
Private Subnet B{{CIDR_PRIV_B}}Application servers (AZ-B)
Isolated Subnet A{{CIDR_ISO_A}}Databases, secrets
Isolated Subnet B{{CIDR_ISO_B}}Databases, secrets (AZ-B)APIs

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.

true(Fly.iostaging),App
Parameter Value
Type {{LB_TYPE}}AWS App Runner (built-in HTTPS proxy)
Protocol HTTPS (TLS 1.2+) — App Runner enforced
SSL Termination At loadApp balancerRunner edge
Health Check Path {{HEALTH_CHECK_PATH}}/api/health
Health Check Interval {{INTERVAL}}s30s
Unhealthy Threshold {{THRESHOLD}}3 consecutive failures
IdleForce TimeoutHTTPS {{TIMEOUT}}s
Stickiness {{STICKINESS}}Runner default

3.3 DNS Architecture

AppRunner
Record Type Value TTL
{{DOMAIN}}getdrop.no (future) A / ALIAS LoadApp BalancerRunner URL {{TTL}}300
api.{{DOMAIN}}Current production CNAME API Load Balancer9ef3szvvsb.eu-west-1.awsapprunner.com {{TTL}}
cdn.{{DOMAIN}}CNAMECDN Distribution{{TTL}}managed

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

ServedfromNext.jsvia
Parameter Value
Provider {{CDN_PROVIDER}}None currently — App Runner serves directly
OriginStatic assets {{CDN_ORIGIN}}
Cache.next/static/ Behaviors StaticApp assets: 1yr, API: no-cache, HTML: 5min
HTTPS OnlyYesRunner
WAF Integration {{WAF_INTEGRATION}}TBD — requires domain + CloudFront setup

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
ClusterService ARN {{CLUSTER_SPEC}}arn:aws:apprunner:eu-west-1:324480209768:service/drop-web/8e45b0d335304487a1880f4e32d6aeec
Node Groups{{NODE_GROUPS}}
Min Nodes{{MIN_NODES}}
Max Nodes{{MAX_NODES}}
Node Size{{NODE_SIZE}}Production
Container Registry {{REGISTRY}}ECR 324480209768.dkr.ecr.eu-west-1.amazonaws.com/drop-web
Service URLhttps://9ef3szvvsb.eu-west-1.awsapprunner.comProduction
Base Imagenode:22-alpineMulti-stage build
Non-root usernextjs (UID 1001)Security hardened
Exposed Port3000
Entrypointnode server.jsNext.js standalone

4.2 Serverless Functions

No

serverlessfunctionscurrently.AlllogicisinNext.jsAPIroutesservedbyApp
Function Trigger Memory Timeout Purpose
{{FUNCTION_1}} {{TRIGGER}}{{MEMORY}}MB{{TIMEOUT}}s{{PURPOSE}}
Runner.

4.3 Instance Sizing & Auto-Scaling

{{DURATION}}min
Service Instance TypePlatform Min Max Scale Trigger
{{SERVICE}}drop-web {{INSTANCE}}App Runner {{MIN}}1 {{MAX}}Auto CPUManaged >by {{CPU}}%App forRunner
drop-stagingFly.io0AutoScales 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
{{DB_NAME}}drop-db (prod) {{ENGINE}}PostgreSQL {{VERSION}}16 {{HOSTING}}AWS RDS {{INSTANCE}}db.t4g.micro {{STORAGE}}GBTBD — requires RDS console check {{HA}}Single-AZ (multi-AZ upgrade planned)
drop-staging (staging)SQLite (better-sqlite3 12.6.2)Fly.io ephemeral volumeNo 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

S3bucketscurrently.apass-throughpaymentappnouserfile
BucketDrop /is Container Purpose Access Lifecycle Encryption
{{BUCKET_NAME}}{{PURPOSE}}{{ACCESS}}{{LIFECYCLE}}AES-256
uploads.

5.3 File Storage

Storage Type Mount Point Purpose Size
{{STORAGE_NAME}}Fly.io Volume (staging) {{TYPE}}Persistent volume {{MOUNT}}/app/data {{PURPOSE}}SQLite database + backups {{SIZE}}GB1GB

6. Security

6.1 Network Security Groups / Firewall Rules

Security Group Direction Port Protocol Source / Destination Purpose
sg-albApp Runner Inbound 443 TCP 0.0.0.0/0 HTTPS from internet
sg-albApp Runner Outbound {{APP_PORT}}5432 TCP sg-appRDS endpoint Forward to app
sg-appInbound{{APP_PORT}}TCPsg-albFrom load balancer
sg-appOutbound{{DB_PORT}}TCPsg-dbDatabasePostgreSQL access
sg-dbApp Runner InboundOutbound {{DB_PORT}}443 TCP sg-app0.0.0.0/0 FromExternal applicationAPIs only(Sumsub, BankID, Slack)

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
AWSManagedRulesKnownBadInputsRuleSetKnown bad inputsBlock
Rate limiting {{RATE_LIMIT}}100 req/5min per IP 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.

keys
Secret Rotation Schedule Access
DatabaseDATABASE_URL credentials(PostgreSQL credentials) 90 days App Runner role only
APIJWT_SECRET 90 (third-party)daysApp Runner role only
SLACK_WEBHOOK_URL On compromise App Runner role only
BANKID_CLIENT_SECRETPer BankID policyApp Runner role only
SUMSUB_API_KEY180 daysApp Runner role only
TLS certificates 60Auto-renewed daysby beforeApp expiryRunner DeployAWS role only
JWT signing key365 daysAuth service onlymanaged

Caching: Secrets cached in-memory for 5 minutes (configurable TTL via initSecrets()).

6.4 IAM Roles & Policies

Role Trusted By Key Permissions Purpose
{{APP_ROLE}}App Runner task role EC2AWS /App ECS TaskRunner SecretsManager:GetSecret,secretsmanager:GetSecretValue, S3:GetObjectecr:GetAuthorizationToken Application runtime
{{DEPLOY_ROLE}}GitHub Actions deploy roleGitHub Actions (OIDC)apprunner:StartDeployment, ecr:PushImage, ecr:BatchCheckLayerAvailability CI/CD ECR:PushImage, ECS:UpdateServiceDeploymentsdeployments
{{BACKUP_ROLE}}RDS monitoring role Lambda / Cronarn:aws:iam::324480209768:role/rds-monitoring-role RDS:CreateSnapshot,rds:DescribeDBInstances, S3:PutObjectCloudWatch metrics BackupsRDS enhanced monitoring

7. Cost Estimation

First
Component Service Spec Est. Monthly Cost
Compute {{SERVICE}}AWS App Runner {{SPEC}}0.25 vCPU / 0.5GB (auto-scale) ~${{COST}}25–50
Database {{SERVICE}}AWS RDS PostgreSQL {{SPEC}}db.t4g.micro, Single-AZ ~${{COST}}15–25
LoadContainer BalancerRegistry {{SERVICE}}AWS ECR {{SPEC}} 500MB free, then ${{COST}}
CDN{{SERVICE}}{{TRAFFIC}}GB transfer${{COST}}
Storage{{SERVICE}}{{CAPACITY}}0.10/GB ~${{COST}}1–5
Monitoring {{SERVICE}}CloudWatch Logs {{METRICS}}App metricsRunner logs~$2–5
BetterStackUptime monitoringFree tier (3 monitors) ${{COST}}0
Total ~${{TOTAL}}43–85/month

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

for
Component HA Strategy Failover Time Notes
Application (App Runner) Multi-AZ,App N+1Runner instancesmanaged multi-instance Immediate (ELB health check)check driven) App Runner restarts unhealthy instances automatically
Database (RDS)Single-AZ with automated backups30 min (snapshot restore) Multi-AZ withplanned auto-failover 60-120 secondsDNS propagationv1.0
CacheStaging (Fly.io) ClusterSingle modemachine, / Replicationscale-to-zero 30Auto-start secondson request RedisNo SentinelHA for staging
CDNExternal services GlobalN/A edge networkno Drop-managed HA TransparentDepends on provider SLA ProviderSumsub, HABankID managed externally

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



Approval

Role Name Date Signature
Author Platform Architect (AI) 2026-02-23
Reviewer
Approver Alem Bašić