Skip to main content

Infrastructure Map — live topology

Generated from the live AWS account, not a hand-drawing — captured on 2026-07-04 with a read-only audit profile (ali-claude) against account 140151801565 in ap-southeast-1. When infra changes, edit the data arrays in src/components/InfraFlow / InfraMap and the diagrams redraw.

How the instances talk to each other

The request flow below is derived from the live security-group rules — each arrow is a port a tier is actually allowed to reach, not an assumption. Internet enters at one point (haproxy :443), fans out to the frontends, which call the APIs, which alone can reach the databases and redis.

🔵 prod-vpc — request flow10.0.0.0/16 · vpc-04c850cec9ed333cc
InternetEdge / SSLFrontendAPIData (private):443:3000:3000:3000:80:3000:8010:8000:8000:8000:80:3306:3306:3306:3306:3306:5432:6379:5432Internetpublicjod.prod.haproxyhaproxy · SSL termjodpro.prod.frontendpm2 static :3000jodgig.prod.frontendnginx/pm2 :3000jodgig.prod.boardnextjs :3000jodapp.prod.webkamal-proxy :80jod.prod.metabaseBI :3000jodpro.prod.apinginx→php-fpm :8010jodgig.prod.api-2nginx→php-fpm :8000jodgig.prod.api-3nginx→php-fpm :8000jodgig.prod.reportsphp-fpm + cronjodapp.prod.apikamal→rails :80RDS jodpromysql :3306RDS jodgigmysql :3306RDS jodgig-replica-1mysql :3306RDS jodgig-reportsmysql :3306RDS jodapppostgres :5432jodapp.prod.redisredis :6379RDS metabasepostgres :5432
Edges are real security-group rules. The API tier sits in the private subnets; only it can reach RDS :3306/:5432 and redis :6379. haproxy in the public subnet is the single internet entry point (:443, SSL terminated). Dashed = read-replica / secondary path.
🟠 qa-vpc — request flow10.1.0.0/16 · vpc-0206f290a44b88e41
InternetEdge / SSLFrontendAPIData (private):443:3010:3000:3000:80:8010:8000:80:3306:3306:3306:5432:6379Internetpublicjod.qa.haproxyhaproxy · SSL termjodpro.qa.frontendpm2 :3010jodgig.qa.frontendnginx :3000jodgig.qa.boardnextjs :3000jodapp.qa.webkamal-proxy :80jodpro.qa.apiphp-fpm :8010jodgig.qa.apiphp-fpm :8000jodapp.qa.apikamal→rails :80RDS jodpro-qamysql :3306RDS jodgig-qamysql :3306RDS jodgig-qa-syncmysql :3306RDS jodapp-qapostgres :5432jodapp.qa.redisredis :6379
Security gaps found in the SGs: rds-jod-test and the DMS groups allow MySQL :3306 from 0.0.0.0/0 (internet-open DB); :22 (SSH) is open to the world on many groups; 85 security groups exist, most launch-wizard-* / awseb-* cruft.
jodapp (Rails/kamal) jodgig (PHP/nginx) jodpro (PHP/nginx) shared (haproxy/BI) internet database (dashed)
Evidence: Strong

ec2 describe-security-groups, 2026-07-04. e.g. prod-vpc.rds.private-subnets permits :3306 only from 10.0.128.0/20 + 10.0.144.0/20 (the private/API subnets); prod.haproxy permits :443 from 0.0.0.0/0; prod-vpc.redis.private-subnet permits :6379 only from the private subnets. On-host processes (haproxy → nginx/php-fpm, kamal-proxy → rails, pm2) are from the deploy setup.

Inventory & cost by VPC

The same fleet, grouped by VPC/subnet with instance types and on-demand cost. Costs are on-demand list price ($/mo at 730h) — priced at full on-demand even though the console shows $0, because NDS's Savings Plan (not yours) currently hides it. For the money side, see Savings Plans & cost optimisation.

⚠ Billing: this account (140151801565 "JOD AWS 2022") is still a member of NDS's AWS Organization — management account 767010522689, org o-0iovcybjee. The self-serve migration has not completed.
NDS owns the Savings Plan (console shows compute at $0) but invoices you full on-demand anyway — proven by the May invoice ($0.3332/hr for c7g.2xlarge). They keep the discount (~$180–240/mo).  Leaving is not a bill shock: you already pay on-demand. Self-serve lets you buy your own Savings Plan + RDS RIs.
Region ap-southeast-1 · AZ 1a / 1b · 26 EC2 (25 running) · 11 RDS · 2 NAT · costs = on-demand list $/mo
🔵 prod-vpc10.0.0.0/16 · vpc-04c850cec9ed333ccigw-00018c35
🌐 NAT nat-03e1ba4b · public-1a · ~$43/mo + datahaproxy EIP 13.251.207.51 → :443 SSL term
prod-subnet-public110.0.0.0/20 · 1apublic
jod.prod.haproxy
t4g.small$15.48
prod-subnet-private110.0.128.0/20 · 1aprivate
jodpro.prod.frontend
c6g.large$57.23
jodgig.prod.frontend
c7g.large$60.81
jodgig.prod.frontend-board
t4g.small$15.48
jodapp.prod.web
t4g.small$15.48
jod.prod.metabase
t3a.large (x86)$68.91
prod-subnet-private210.0.144.0/20 · 1bprivate
jodpro.prod.api
c6g.large$57.23
jodgig.prod.api-2rightsize
c7g.2xlarge$243.24
jodgig.prod.api-3rightsize
c7g.2xlarge$243.24
jodgig.prod.reports
c7g.large$60.81
jodgig.prod.cron
t4g.medium$30.95
jodapp.prod.api
t4g.small$15.48
jodapp.prod.redis
t4g.small$15.48
jodapp.prod.deploy
t4g.small$15.48
RDS · prod-jodpro-rds-subnet (private)
jodgigwhale
db.m7g.2xlarge · mysql$674.64
jodgig-replica-1
db.t4g.medium · mysql$73.44
metabase
db.t4g.medium · postgres$73.44
jodapp
db.t4g.small · postgres$36.72
jodpro
db.t4g.micro · mysql$18.00
jodgig-reports
db.t4g.micro · mysql$18.00
🟠 qa-vpc10.1.0.0/16 · vpc-0206f290a44b88e41igw-00e82984
🌐 NAT nat-02f932aa · public-1a · EIP 3.1.123.87 · ~$43/mo + data
qa-subnet-public110.1.0.0/20 · 1apublic
jod.qa.haproxy
t4g.micro$7.74
qa-subnet-private1-1a10.1.128.0/20 · 1aprivate
jodpro.qa.frontend
t4g.medium$30.95
jodgig.qa.frontend
t4g.medium$30.95
jodgig.qa.frontend-board
t4g.small$15.48
jodapp.qa.web
t4g.micro$7.74
jodapp.qa.deploy
t4g.small$15.48
qa-subnet-private1-1b10.1.144.0/20 · 1bprivate
jodpro.qa.api
t4g.medium$30.95
jodgig.qa.api
t4g.medium$30.95
jodapp.qa.api
t4g.small$15.48
jodapp.qa.redis
t4g.micro$7.74
RDS · qa subnet groups (private, except noted)
jodgig-qa
db.t4g.medium · mysql$73.44
jodgig-qa-sync
db.t3.small · mysql (gp2)$37.44
jodpro-qa
db.t4g.micro · mysql$18.00
jodapp-qapublic
db.t4g.micro · postgres$18.00
🔴 default-vpc172.31.0.0/16 · vpc-03bac4a69dede2b23peered → prod & qa
default subnets172.31.x · 1a/1b/1cpublic
JodProDockerDemoV2idle
t3.small$19.27
JODv2-REPORT-PROD-CRON-envidle
t3.micro · stopped$0.00
rds-jod-test-reportspublic
db.t3.micro · mysql (gp2)$18.72
VPC peering: prod-vpc ↔ default-vpc (pcx-07edcda1) · qa-vpc ↔ default-vpc (pcx-0040e708).  Production is peered to a default VPC holding a public test DB + demo box — a flat blast radius worth severing.
jodapp jodgig jodpro jod / sharedrightsize <3% CPU, downsizewhale 64% of RDS $public internet-reachable DBidle unused / stop candidate

What the map is telling us

Evidence: Strong

AWS Cost Explorer + EC2/RDS describe APIs, account 140151801565, 2026-07-04. 26 EC2 instances (25 running, 1 stopped), 11 RDS instances, 2 NAT gateways, 7 Elastic IPs. Zero Savings Plans or Reserved Instances owned by this account.

Three VPCs, two of them real. prod-vpc (10.0.0.0/16) and qa-vpc (10.1.0.0/16) mirror each other cleanly — public subnet with an haproxy + NAT gateway in front, private subnets holding the jodapp / jodgig / jodpro app tiers, and a private RDS subnet group behind them. The default-vpc (172.31.0.0/16) is the odd one out: it holds only a demo box, a stopped cron, and a publicly reachable test database — yet it is VPC-peered into both prod and qa.

Evidence: Strong

rds describe-db-instances: jodapp-qa and rds-jod-test-reports both report PubliclyAccessible=true. ec2 describe-vpc-peering-connections: pcx-07edcda1 (prod↔default) and pcx-0040e708 (qa↔default) are both active.

The cost is lopsided toward two things. One database (jodgig, db.m7g.2xlarge) is 64% of all RDS instance spend, and two application servers (jodgig.prod.api-2 / api-3, both c7g.2xlarge) sit at effectively idle CPU. These are the "whale" and "rightsize" badges on the map.

Evidence: Strong

CloudWatch CPUUtilization, 30-day (2026-06-04 → 07-04): jodgig.prod.api-2 avg 0.10% / max 12.6%; jodgig.prod.api-3 avg 2.68% / max 17.1% — both on 8-vCPU c7g.2xlarge. RDS jodgig avg 20.3% / max 98.9%.

You are not billing directly with AWS yet — and NDS keeps your Savings Plan discount. This account is still a member of NDS's AWS Organization (o-0iovcybjee). Your 25 EC2 instances read $0/hr in the console because NDS's Savings Plan covers them from the payer account — but the NDS invoice bills you the full on-demand rate for that same compute ($0.3332/hr for c7g.2xlarge on the May bill). The discount is theirs, not yours. Leaving is therefore not a bill shock: you already pay on-demand. Self-serve simply lets you buy your own Savings Plan and recapture what NDS keeps (~$180–240/mo).

Evidence: Strong

NDS invoice 202605000003 (May 2026): total $2,895.42; EC2 "Compute Instance" billed entirely at On-Demand rates ($1,135.18), matching the AWS Pricing API exactly. Meanwhile Cost Explorer shows SavingsPlanCoveredUsage $1,097.71 canceled by SavingsPlanNegation -$1,097.71 — a payer-owned plan. Same compute, two views: AWS covers it for NDS; NDS charges you list.

Architecture smells worth a ticket

SmellWhereWhy it matters
Public databasesjodapp-qa, rds-jod-test-reportsInternet-reachable DBs on a jobs platform holding applicant data
Default VPC peered to prodpcx-07edcda1, pcx-0040e708Flat blast radius; default VPCs should be isolated or deleted
Idle c7g.2xlarge ×2jodgig.prod.api-2/3~$400/mo on-demand at ~1% CPU — 2–3 sizes too big
gp2 storage remnantsjodgig-qa-sync, rds-jod-test-reports, 1 EBS volgp2 is ~20% dearer than gp3 for the same performance
Idle Elastic IPs3 unassociated + 1 on stopped boxBillable idle IPv4 (~$3.60/mo each)

All of these are addressed with concrete numbers and a sequenced plan in Savings Plans & cost optimisation.