010. Network topology: no IGW, no NAT (for now)
- Date: 2026-06-03
- Status: Proposed
Decision
Section titled “Decision”In the context ofrunning rag-sample as a personal demo with per-environment VPCs (RAG001), Aurora Serverless v2 atmin_capacity = 0, and a tight idle-cost target,facingthe choice of what egress paths the VPC offers to anything that might run inside it, and what entry paths it offers to anything outside,we decided fora “for now” preference of zero VPC-internet boundary infrastructure: no Internet Gateway, no NAT Gateway, no PrivateLink endpoints, in either QA or Prod; the per-env VPC carries only private subnets, an Aurora subnet group, and a security group for the Aurora cluster,and neglected- NAT Gateway in each VPC, which is the standard pattern for letting in-VPC workloads reach AWS service public endpoints, at roughly $32 per AZ per month idle plus data-transfer fees, doubled across two environments,
- PrivateLink interface endpoints per AWS service (Bedrock runtime, Secrets Manager, possibly Aurora Data API if a future component runs in-VPC), at roughly $7.30 per AZ per service per month, scaling linearly with both the service list and the AZ count,
- a public-subnet posture with workloads given public IPs, which avoids NAT but broadens the inbound attack surface for no operational gain at this scale,
to achieve- the scale-to-zero idle cost the demo depends on; the alternatives below are the largest single fixed-cost lines this stack would carry,
- a minimal network-control-plane surface to reason about, which suits a single operator,
- the freedom to defer the VPC-vs-no-VPC posture of each application component until that component actually exists; today the only thing inside the VPC is the Aurora cluster itself,
accepting- that any application component that needs to run inside the VPC is blocked from reaching AWS services until this decision is revisited; this is enforced by absence, not by policy,
- that the Aurora cluster is reachable only via paths that do not require in-VPC origin (today, the RDS Data API public endpoint, see RAG011),
- that adding a future in-VPC workload triggers the reversal procedure below, not a quiet
terraform apply, - that “for now” means the demo phase; a genuine production deployment would re-evaluate this against the residency, egress-control, and observability requirements of that environment.
Consequences
Section titled “Consequences”The per-env VPC contains only what Aurora itself requires: two private subnets across two AZs, an Aurora subnet group, and an Aurora security group whose only inbound rule is from intra-VPC source CIDRs (which today admits nothing, since nothing else runs in the VPC). There is no aws_internet_gateway, no aws_nat_gateway, no aws_vpc_endpoint. The network topology document captures this layout end-to-end (network topology).
The data-access path for application components is constrained by this decision: any component running outside the VPC must reach Aurora through a public-endpoint contract, and any component running inside the VPC would need a way out to Bedrock and Secrets Manager that this ADR does not provide. The choice of which contract to standardize on is made separately in RAG011; this ADR does not pick it.
CI deployment of Terraform runs from GitHub-hosted runners against the AWS APIs directly; no self-hosted runner inside the VPC is required, so the absence of NAT does not affect the deploy path.
Observability (RAG008) flows out of Lambda directly to CloudWatch, X-Ray, and Bedrock via their public endpoints from the Lambda execution environment, which is itself outside the VPC. No VPC-side observability infrastructure (VPC Flow Logs to S3, GuardDuty VPC findings, etc.) is provisioned beyond AWS defaults.
Cost comparison
Section titled “Cost comparison”The table below compares the per-environment monthly idle cost of the realistic options, before data-transfer fees. Doubled across QA and Prod for the running total.
| Option | Per env, monthly idle | Two envs, monthly idle |
|---|---|---|
| No IGW, no NAT, no endpoints (chosen) | $0 | $0 |
| NAT Gateway, 1 AZ | ~$32 | ~$64 |
| NAT Gateway, 2 AZ (HA) | ~$64 | ~$128 |
| PrivateLink, 1 AZ, per service required | ~$7.30 | ~$14.60 |
| PrivateLink, 2 AZ, per service required | ~$14.60 | ~$29.20 |
| Public subnets, public IPs on workloads | ~$0 | ~$0 |
A realistic in-VPC posture for this stack would need Bedrock Runtime and Secrets Manager at minimum, often Aurora Data API if used from in-VPC, and frequently S3 (gateway endpoint is free). A 2-AZ PrivateLink posture across three required services across two environments lands near $90/month idle, larger than the current entire AWS bill estimate.
The public-subnet option is rejected for security reasons (inbound attack surface, the deny-all-by-absence property of the current design).
Reversal procedure
Section titled “Reversal procedure”This decision is “for now.” It must be revisited, and this ADR must be amended or superseded, before any of the following occur:
- A workload that must run inside the VPC and reach Bedrock, Secrets Manager, or any AWS service whose endpoint is public. Choose NAT or per-service PrivateLink and amend the cost model in cost.
- A data-residency, egress-allowlisting, or network-DLP requirement that mandates outbound traffic from any AWS-side workload traverse a controlled egress. Same choice as above, plus probable route-table changes.
- A move to dedicated AWS accounts per environment (the migration path noted in RAG001), which is the natural moment to reset this decision against the production-grade requirements of that environment.
- The introduction of a self-hosted CI runner or a long-running process for observability ingestion, both of which historically pressure teams into adding NAT.
When reversal happens, the work looks like this:
- Pick NAT, per-service PrivateLink, or a mix. Capture the choice and the per-service inclusion list in a new ADR.
- Add the corresponding Terraform to the
networkmodule behind per-env feature flags so QA can adopt it before Prod, or so one env can pilot while the other holds. - Update Budgets thresholds (#33) to absorb the new fixed line.
- Update
architecture/cost.mdandarchitecture/network-topology.mdto reflect the new picture. - Re-evaluate RAG011; the data-access contract may or may not change, but its constraints will.
This ADR extracts and replaces the network-topology rationale that previously lived in RAG001’s Notes section. RAG001 is about environment separation (two envs, separate VPCs, gated promotion). The reasoning about IGW, NAT, PrivateLink, and the data-access path is its own decision and belongs here. RAG001’s content has been amended to reflect that split.
© 2026 Benjamin Arunski