AWS Networking for Aurora RDS PostgreSQL
Running PostgreSQL in AWS isn’t just about creating the database — you first build the network it sits in. With Aurora RDS, you are the network engineer: you decide the IP range, which parts are reachable from the internet, what traffic is allowed between resources, and which data centers your database runs in. This post walks through the core networking concepts — VPC, subnets, gateways, route tables, security groups, and DB subnet groups — that you need to understand before provisioning Aurora RDS PostgreSQL.
IP Addressing Fundamentals
Before diving into AWS-specific concepts, a quick refresher on the addressing system everything is built on.
CIDR Notation
CIDR notation defines a range of IP addresses. The number after the slash is how many bits are fixed — more fixed bits means fewer addresses:
1
2
3
10.0.0.0/16 = 10.0.*.* = 65,536 IPs
10.0.1.0/24 = 10.0.1.* = 256 IPs
10.0.2.0/24 = 10.0.2.* = 256 IPs
/16= first 16 bits fixed (first two octets) = 65,536 IPs/24= first 24 bits fixed (first three octets) = 256 IPs/32= all bits fixed = exactly 1 IP
Private IP Ranges
Three ranges are globally reserved for private networks (RFC 1918). Every router and OS knows these will never appear on the public internet:
| Range | Size |
|---|---|
10.0.0.0/8 |
16.7 million IPs |
172.16.0.0/12 |
~1 million IPs |
192.168.0.0/16 |
65,536 IPs |
Using a public range internally (say 142.250.80.0, which belongs to Google) would work — until you need to reach the real owner of that range. Internal traffic would route locally instead of reaching the real destination. This is called IP shadowing and it’s why you always use private ranges for internal networks.
Ingress and Egress
Two terms that appear everywhere in AWS docs and Terraform configs: ingress means inbound traffic (coming in), egress means outbound traffic (going out). The plain English equivalents — inbound and outbound — mean the same thing.
VPC — Virtual Private Cloud
A VPC is your own isolated network inside AWS. Think of it as your private data center’s network, built on the same private IP ranges described above.
When you create a VPC you assign it a CIDR block — for example, 10.0.0.0/16 gives you 65,536 addresses. AWS requires VPC CIDR blocks to be between /16 and /28.
The CIDR block is just the starting point. A VPC is the container for everything else: subnets, gateways, route tables, and security groups. The IP range defines the address space; the rest defines what can talk to what and how.
Which Private Range to Use?
10.0.0.0/8— most common for cloud and corporate networks. AWS defaults to this. Plenty of room to carve out many VPCs without overlapping.172.16.0.0/12— sometimes used as a second range when10.xis already taken.192.168.0.0/16— home routers default to this. Rarely used in cloud because it’s small and likely to collide if you ever connect a VPC to an office network.
The practical concern is overlap. If a VPC uses 10.0.0.0/16 and an office network also uses 10.0.0.0/16, they can’t be connected via VPC peering or VPN because the ranges collide. Companies often plan CIDR allocations across environments to avoid this — for example, 10.0.0.0/16 for prod, 10.1.0.0/16 for staging, 10.2.0.0/16 for dev.
Subnets
A subnet is a subdivision of the VPC tied to a specific Availability Zone (AZ). A VPC spans a whole region (e.g., us-east-1), but each subnet lives in exactly one AZ (e.g., us-east-1a). AZs are physically separate data centers.
Public vs Private Subnets
This distinction is not an inherent property — it’s a consequence of routing. A subnet is “public” because its route table has a route to an Internet Gateway. A subnet is “private” because it doesn’t. The labels are conventions based on how traffic flows.
Even in a “public” subnet, resources aren’t automatically exposed. A NAT Gateway in a public subnet has a public IP but only allows outbound traffic — nobody can initiate a connection to it from outside. A bastion host accepts inbound SSH but only from a specific IP, locked down by its security group.
Why Aurora RDS Needs Multiple Subnets
AWS requires the DB subnet group to span at least two AZs. This is for high availability — if one data center goes down, the database can failover to the other AZ. Aurora takes this further: the storage layer automatically replicates data six ways across three AZs, so having subnets in multiple AZs is essential.
1
2
3
4
5
VPC: 10.0.0.0/16
├── Subnet A (private): 10.0.1.0/24 in us-east-1a
├── Subnet B (private): 10.0.2.0/24 in us-east-1b ← Aurora needs both
├── Subnet C (public): 10.0.101.0/24 in us-east-1a ← for bastion/NAT
└── Subnet D (public): 10.0.102.0/24 in us-east-1b
Internet Gateway and NAT Gateway
Internet Gateway (IGW) attaches to the VPC and allows resources in public subnets to communicate with the internet — both inbound and outbound.
NAT Gateway sits in a public subnet and allows resources in private subnets to make outbound requests (download patches, call external APIs) without being reachable from the internet. It’s a one-way door.
1
2
3
4
5
6
7
Internet
│
▼
Internet Gateway ─── Public Subnet ─── NAT Gateway
│
▼
Private Subnet ─── Aurora RDS
Aurora RDS should never be in a public subnet. If it needs to reach the internet (which is rare — AWS handles engine updates through its own management plane), it goes through the NAT Gateway.
Do You Always Need Public Subnets?
No. The bastion-plus-public-subnet pattern is the simplest way to get started, but if you use alternatives like a VPN, AWS Systems Manager Session Manager, or an application server in the same VPC, you don’t need a bastion or any public subnets at all. The entire VPC can be private — you still need at least two private subnets in different AZs for the DB subnet group, but nothing needs to be public.
Route Tables
Route tables are the rules that tell traffic where to go. Each subnet is associated with exactly one route table.
Public subnet route table:
| Destination | Target | Meaning |
|---|---|---|
10.0.0.0/16 |
local |
Traffic within VPC stays local |
0.0.0.0/0 |
igw-xxxxx |
Everything else goes to the internet |
Private subnet route table:
| Destination | Target | Meaning |
|---|---|---|
10.0.0.0/16 |
local |
Traffic within VPC stays local |
0.0.0.0/0 |
nat-xxxxx |
Outbound goes through NAT (no inbound) |
The only difference is one line: igw vs nat for the default route.
Security Groups
A security group is a virtual firewall attached to a resource — an EC2 instance, an Aurora RDS cluster, etc. It controls which traffic is allowed in (ingress) and out (egress).
Security groups are a separate layer from routing. Routing controls whether traffic can physically reach a resource; security groups control whether the resource accepts that traffic. Both must allow the traffic for it to get through.
Key Behaviors
- Stateful: if you allow inbound traffic, the response is automatically allowed out (unlike NACLs, which are stateless).
- Default deny inbound: nothing can reach the resource unless you add a rule.
- Default allow outbound: the resource can talk to anything unless you restrict it.
- Source can be another security group: this is the powerful part.
Referencing Security Groups Instead of IPs
When the DB security group says “allow ingress from sg-app,” it means “allow ingress from any resource that has sg-app attached.” You don’t need to track IPs — security group references don’t break when IPs change:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
resource "aws_security_group" "app" {
vpc_id = aws_vpc.main.id
name = "app"
}
resource "aws_security_group" "db" {
vpc_id = aws_vpc.main.id
name = "database"
ingress {
from_port = 5432
to_port = 5432
security_groups = [aws_security_group.app.id]
}
}
The app server gets aws_security_group.app attached, the Aurora cluster gets aws_security_group.db. Only resources in the app group can reach the database on port 5432.
DB Subnet Group
A DB subnet group is an RDS-specific concept — a named collection of subnets where RDS is allowed to place database instances. When you create an Aurora cluster, you don’t pick a subnet directly. You pick a DB subnet group, and Aurora chooses which subnets to use. For Multi-AZ deployments it places the writer in one subnet and readers in others.
The rule: the group must include subnets in at least two different AZs.
Putting It All Together
Here’s the complete picture for an Aurora RDS PostgreSQL deployment:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
┌─────────────────────── VPC (10.0.0.0/16) ────────────────────────┐
│ │
│ ┌── Public Subnet (10.0.101.0/24, us-east-1a) ────┐ │
│ │ Bastion Host (EC2) │ │
│ │ SG: allow SSH (22) from your IP │ │
│ └─────────────────────────────────────────────────┘ │
│ │ connects via private IP on port 5432 │
│ ▼ │
│ ┌── Private Subnet (10.0.1.0/24, us-east-1a) ─────┐ │
│ │ Aurora PostgreSQL (writer) │ │
│ │ SG: allow 5432 from bastion SG │ │
│ └─────────────────────────────────────────────────┘ │
│ │
│ ┌── Private Subnet (10.0.2.0/24, us-east-1b) ───────┐ │
│ │ Aurora PostgreSQL (reader replica) │ │
│ │ Same SG │ │
│ └───────────────────────────────────────────────────┘ │
│ │
│ DB Subnet Group = [private-subnet-1a, private-subnet-1b] │
└──────────────────────────────────────────────────────────────────┘
You SSH into the bastion, then from there psql into the Aurora endpoint. The security group on Aurora only allows connections from the bastion’s security group on port 5432. The database is never directly reachable from the internet.
Each layer has a distinct job: the VPC defines the address space, subnets partition it across AZs, route tables control where traffic flows, security groups decide what gets through, and the DB subnet group tells Aurora which subnets it can use. All of these must be in place before you create the first Aurora cluster.