A subnet called “public-a” can be private. A database with a public IP address can be unreachable from the internet. A VPC with an internet gateway attached can have no public subnets at all. None of those things decides whether a subnet is public. One thing does, and it is the thing people forget to look at: the route table the subnet uses.
The definition, in one line
The Amazon VPC User Guide defines the subnet types by their routing. A public subnet “has a direct route to an internet gateway”. A private subnet “does not have a direct route to an internet gateway” and needs a NAT device to reach the internet. A VPN-only subnet routes to a virtual private gateway, and an isolated subnet has no routes outside its VPC at all.
So “public” is not a setting on the subnet. It is a consequence of one route, in one table: a destination such as 0.0.0.0/0 (::/0 for IPv6) whose target is an internet gateway (igw-...).
Follow the packet
Picture a customer on the internet opening a connection to a web server in your VPC. For the request to arrive and the reply to get home, four things must hold:
- An internet gateway is attached to the VPC. It is the VPC’s two-way door to the internet, and for IPv4 it does the one-to-one translation between the instance’s public and private addresses.
- The instance has a public IPv4 address or an Elastic IP address. A private address such as
10.0.1.10cannot be routed on the internet. - The route table of the instance’s subnet sends internet-bound traffic to the internet gateway. Without that route the reply has nowhere to go.
- Security groups and network ACLs allow the traffic.
Miss the third and nothing else helps. The VPC guide says it directly: instances in a subnet with no route to the internet gateway “can’t communicate with the internet, even if they have public IP addresses”. A public IP is an address, not a path.
That also explains the comforting surprise in the other direction. A database that was launched with a public IP by accident is still out of reach while its subnet has no internet route. It is a bad idea to leave the address there, but it is the route table, not the address, that keeps it private.
The main route table trap
Every subnet uses exactly one route table. You can associate a subnet with a table explicitly. If you do not, the subnet is implicitly associated with the VPC’s main route table, which, in the guide’s words, “controls the routing for all subnets that are not explicitly associated with any other route table”.
Now the trap. Suppose you need one web subnet to be public, and you add 0.0.0.0/0 → igw to the main route table because it is the table already there. The web subnet becomes public. So does every other subnet that has no table of its own, including the database’s subnet, and including any subnet anyone creates next month. One route, and the database you meant to hide answers the internet.
One way the guide suggests protecting a VPC is to leave the main route table of a nondefault VPC in its original state, with only the local route, and give each public subnet a custom route table that holds the internet route. (A default VPC is the exception: its main route table already has the internet route, so its default subnets are public.) Then a forgotten association fails closed. A new subnet starts private, and someone has to decide, on purpose, to make it public.
Two smaller facts complete the picture:
- The local route connects every subnet of the VPC. Every route table has a local route for the VPC’s CIDR, so a web server in one subnet reaches a database in another without any extra route, peering or gateway.
- The most specific route wins. When several routes match a destination, the VPC uses the longest prefix match, so the local route keeps VPC traffic inside the VPC even when
0.0.0.0/0points at the internet gateway.
What the exam guides ask
The SAA-C03 guide asks you to design secure workloads (Task 1.2) and high-performing network architectures (Task 3.4); the SOA-C03 guide asks you to configure a VPC (Skill 5.1.1). A scenario on this topic usually hands you an instance that cannot be reached and a list of fixes, and the wrong answers are tempting because each one sounds like “more internet”:
- Attach another internet gateway. You can attach only one internet gateway to a VPC at a time, and it is already there.
- Add a NAT gateway. A NAT gateway lets instances in private subnets start connections out. Nothing on the internet can start a connection in through it, so a customer still cannot reach the server.
- Allocate an Elastic IP. It is another public address. The instance already has one; it still has no route.
The answer that works is the route: 0.0.0.0/0 to the internet gateway, in the route table that subnet actually uses. And if two answers both work, one adding the route to the main route table and one using a custom route table for the public subnet, the custom route table is the more secure design with the same result.
When you troubleshoot for real, walk the same list in order: is an internet gateway attached, does the subnet’s own route table send 0.0.0.0/0 to it, does the instance have a public or Elastic IP, and do the security group and network ACL allow the traffic.
Play it
The free stage below is this article as a build. Crumb & Co.’s VPC already holds a database with a public IP that must stay private. You place a web server, an internet gateway and a route table, then send traffic and watch where each packet stops. Put the internet route in the main route table and the simulator shows a customer reaching the database, with the reason. Fix it, and the debrief explains why the custom route table is the better answer.