All writing

Public and private subnets are about routing, not privacy

In Amazon VPC, public and private describe subnet routing rather than workload confidentiality. A public subnet has a direct route to an internet gateway; a private subnet does not. Actual exposure depends on addressing, security controls, listeners and the complete network path.

about 23 minutes min read

Two subnets use the same CIDR size.

10.64.0.0/24

10.64.1.0/24

Both contain 256 IPv4 address positions.

Both sit inside the same VPC.

Both use security groups.

Both may contain EC2 network interfaces.

The first subnet uses this route table:

Destination          Target

10.64.0.0/16         local
0.0.0.0/0            igw-0123456789abcdef0

The second uses:

Destination          Target

10.64.0.0/16         local
0.0.0.0/0            nat-0123456789abcdef0

The first subnet is public.

The second is private.

The distinction was not created by:

  • the CIDR
  • the resource type
  • the subnet name
  • a security-group rule
  • the presence of a database
  • the sensitivity of the workload

It was created by the next hop used for internet-bound traffic.

AWS defines a public subnet as one with a direct route to an internet gateway. A private subnet has no direct route to an internet gateway. AWS also distinguishes VPN-only and isolated subnets through their routing behaviour.

Public and private are routing descriptions. They are not security classifications.

That wording sounds almost pedantic.

It prevents a surprising number of architecture mistakes.

A subnet does not contain a privacy switch

An Amazon VPC subnet has:

  • an IPv4 and possibly IPv6 CIDR
  • one Availability Zone
  • an associated route table
  • an associated network ACL
  • address-assignment attributes
  • resources using addresses inside it

The subnet’s type is determined by how its traffic is routed. AWS lists public, private, VPN-only and isolated as routing-derived subnet types.

There is no permanent internal property that says:

privacy = true

and then protects every future resource regardless of later route changes.

A subnet called:

bfstore-prod-private-a

becomes public from a routing perspective if its effective route table gains:

0.0.0.0/0 → internet gateway

A subnet called:

bfstore-prod-public-a

is not public if its effective route table contains only:

10.64.0.0/16 → local

Names document intent.

Route associations create behaviour.

SUBNET NAME

what the architecture intended
        │
        ▼
ROUTE TABLE ASSOCIATION

which table actually applies
        │
        ▼
ROUTES

where traffic actually goes

Reviewing a subnet therefore requires examining its effective route table rather than trusting a tag written several months ago.

Public means a direct internet-gateway route

For IPv4, the characteristic public-subnet route is:

0.0.0.0/0 → igw-id

The destination 0.0.0.0/0 matches every IPv4 address for which no more-specific route exists.

The internet gateway is the direct target.

A minimal public route table might be:

Destination          Target

10.64.0.0/16         local
0.0.0.0/0            igw-0123456789abcdef0

The local route handles destinations inside the VPC.

The default route sends other IPv4 destinations to the internet gateway.

RESOURCE
   │
   ▼
SUBNET ROUTE TABLE
   │
   ├── VPC destination → local
   │
   └── other IPv4      → internet gateway

This classifies the subnet’s path.

It does not prove that every resource inside it can communicate over the public internet or accept internet-initiated traffic.

Those are separate questions.

Routing does not equal exposure

A direct route to an internet gateway is one part of internet connectivity.

For an EC2 instance to initiate IPv4 communication through that gateway, the path normally requires:

DIRECT IPV4 INTERNET EGRESS

internet gateway attached
        │
        ▼
subnet route points to gateway
        │
        ▼
resource has public IPv4 addressing
        │
        ▼
outbound security controls permit traffic

The resource does not need a local listening service merely to initiate an outbound HTTPS request. The remote destination needs to listen.

Internet-initiated inbound reachability has a different chain:

INTERNET-INITIATED INBOUND REACHABILITY

public IPv4 identity
        │
        ▼
direct internet-gateway route
        │
        ▼
inbound security controls permit traffic
        │
        ▼
application is listening

A public subnet supplies the direct route.

It does not grant public exposure by declaration.

Suppose an EC2 instance is placed in a public subnet and has only:

10.64.0.20

as a private IPv4 address.

It has no public IPv4 address and no Elastic IP.

The route table sends public destinations towards the internet gateway, but the instance has no public IPv4 identity through which to communicate directly over the IPv4 internet.

The instance might instead have a public address but a security group allowing inbound traffic only from:

198.51.100.24/32

on:

tcp/443

It is in a public subnet.

It is not open to every source on every port.

PUBLIC SUBNET
      │
      ▼
DIRECT INTERNET ROUTE

does not automatically mean

PUBLICLY REACHABLE RESOURCE

Security groups operate at the resource level.

Network ACLs operate at the subnet level.

Route tables select network paths.

The application decides whether a listening service accepts the connection.

ROUTE

Can the packet reach
the relevant boundary?


SECURITY CONTROL

May the packet cross
that boundary?


APPLICATION

Will the destination
accept it?

The same distinction applies to trust.

A carefully controlled internet-facing load balancer belongs in public subnets because its role requires public routing. Requests arriving through it remain untrusted until DDoS controls, web application firewall rules, TLS, authentication, authorisation and application policy evaluate them.

Traffic from a private subnet is not automatically trustworthy either.

The route explains how a request arrived. Identity, policy and application context decide what it may do.

Public IPv4 auto-assignment is a different subnet setting

A subnet has an attribute controlling whether EC2 instances launched into it request a public IPv4 address for their primary network interface.

That attribute is independent of the subnet’s routing classification. It does not automatically assign public IPv4 addresses to arbitrary secondary network interfaces.

This creates four useful combinations.

Direct internet-gateway route Auto-assign public IPv4 Result
Yes Yes Eligible EC2 instances commonly receive a public IPv4 address on their primary interface and have a direct route
Yes No Public subnet, but resources need explicit public addressing for direct IPv4 internet access
No Yes An instance may receive a public address, but the subnet has no direct route to the internet gateway
No No No direct route and no automatic public IPv4 assignment

The third combination is particularly educational.

PUBLIC IPv4 ADDRESS
        +
NO ROUTE TO INTERNET GATEWAY
        =
NO DIRECT INTERNET PATH

The public address does not drag a route table behind it.

Similarly:

INTERNET-GATEWAY ROUTE
        +
NO PUBLIC IPv4 ADDRESS
        =
NO DIRECT PUBLIC IPv4 IDENTITY

Addressing and routing must agree.

For private subnet classes, I would disable automatic public IPv4 assignment even though routing remains the primary classification. The setting reinforces intent and reduces confusion during troubleshooting.

Defence in depth is often several small controls agreeing politely.

Private is not the same as isolated

A private subnet has no direct route to an internet gateway.

That does not mean it has no path beyond the VPC.

Consider these route tables.

Private with NAT egress

Destination          Target

10.64.0.0/16         local
0.0.0.0/0            nat-0123456789abcdef0

Private with central network routing

Destination          Target

10.64.0.0/16         local
10.0.0.0/8           tgw-0123456789abcdef0
0.0.0.0/0            tgw-0123456789abcdef0

Private with partner connectivity

Destination          Target

10.64.0.0/16         local
172.20.0.0/16        pcx-0123456789abcdef0

Isolated

Destination          Target

10.64.0.0/16         local

The first three have no direct route to an internet gateway.

They are private.

They are not isolated.

They can reach destinations outside the VPC through NAT, Transit Gateway, peering, VPN or another routed target.

An isolated subnet has no route to a destination outside the VPC. It may still reach an interface endpoint through the VPC’s local route because the endpoint is represented by private network interfaces inside the VPC.

Gateway endpoints are different. They add explicit service-prefix-list routes for services such as Amazon S3 or DynamoDB and should be evaluated as their own form of private service reachability.

Calling every non-public subnet private hides important differences in network reach.

A more precise subnet vocabulary

Suppose bfstore, the fictional organisation used throughout this series, wants subnet names that describe routing intent rather than imply a security verdict.

A useful vocabulary is:

Subnet class Direct IGW route Other egress Typical use
public-entry Yes Direct internet Internet-facing load balancer
public-egress-zonal Yes Direct internet Zonal public NAT gateway
private-application No NAT or central egress EKS nodes and Pods
private-platform No Endpoints and selected egress Controllers and platform agents
private-transit No Transit Gateway Network attachments
isolated-data No VPC-local and approved private-service paths RDS and data-tier resources
isolated-control No Narrow internal management paths Sensitive control components

A Regional NAT Gateway does not require a public-egress-zonal subnet, so that class exists only when the zonal NAT architecture is selected.

The exact route tables decide whether each name remains truthful.

subnet_class: private-application

routes:
  - destination: 10.64.0.0/16
    target: local

  - destination: 0.0.0.0/0
    target: transit-gateway

  - destination: s3-prefix-list
    target: s3-gateway-endpoint

controls:
  auto_assign_public_ipv4: false

The name communicates intent.

The route definition proves it.

Private egress has several valid architectures

A private subnet can initiate external communication without becoming public.

The relevant question is not:

Does this subnet use the internet?

It is:

Which next hop provides the path?

Zonal public NAT gateway

The familiar IPv4 pattern sends a private subnet’s default route to a public NAT gateway:

PRIVATE APPLICATION SUBNET
          │
          ▼
0.0.0.0/0 → zonal NAT gateway
          │
          ▼
PUBLIC EGRESS SUBNET
          │
          ▼
0.0.0.0/0 → internet gateway
          │
          ▼
INTERNET

A zonal public NAT gateway:

  • is created in a public subnet
  • uses Elastic IP addressing for public internet access
  • requires the NAT subnet to route towards the internet gateway
  • should normally be paired with same-Availability-Zone private routing when used across several zones

For resilient zonal NAT architecture:

AZ A PRIVATE → AZ A NAT → internet gateway

AZ B PRIVATE → AZ B NAT → internet gateway

AZ C PRIVATE → AZ C NAT → internet gateway

The private subnet remains private because its route points to the NAT gateway, not directly to the internet gateway.

Regional NAT Gateway

AWS also provides Regional NAT Gateway availability mode.

A Regional NAT Gateway:

  • is a VPC-level resource
  • has its own route table
  • does not need a public subnet to host it
  • uses one NAT gateway ID across Availability Zones
  • automatically expands and contracts with the workload footprint to preserve zonal affinity

Regional mode is intended for public egress. It does not provide private NAT connectivity; private NAT use cases continue to use zonal NAT gateways.

Conceptually:

PRIVATE SUBNETS
      │
      ▼
0.0.0.0/0 → Regional NAT Gateway
      │
      ▼
REGIONAL NAT ROUTING
      │
      ▼
INTERNET GATEWAY
      │
      ▼
INTERNET

This model changes the old statement that every public NAT architecture needs a dedicated public subnet in each Availability Zone.

The correct statement is narrower:

A zonal public NAT gateway needs a public subnet. A Regional NAT Gateway does not.

Centralised egress

In a multi-account design, workload VPCs may send default traffic to a Transit Gateway or other central network path:

WORKLOAD PRIVATE SUBNET
          │
          ▼
TRANSIT GATEWAY
          │
          ▼
CENTRAL INSPECTION
          │
          ▼
CENTRAL NAT
          │
          ▼
INTERNET GATEWAY

The workload subnet remains private because it has no direct route to an internet gateway.

The path is longer and requires deliberate design across:

  • workload route tables
  • Transit Gateway attachment associations
  • Transit Gateway route tables
  • inspection routing
  • egress VPC routes
  • return paths
  • Availability Zones
  • cost
  • failure isolation

The word private explains almost none of that.

The path diagram does.

NAT changes source addressing, not the subnet classification

With a zonal public NAT gateway, a private workload sends traffic using its private IPv4 address.

The NAT gateway translates that source to the NAT gateway’s private address. The internet gateway then maps the public NAT gateway’s private address to its Elastic IP for internet communication.

10.64.20.34
private workload
      │
      ▼
10.64.0.10
NAT private address
      │
      ▼
203.0.113.20
NAT Elastic IP
      │
      ▼
internet destination

Responses follow the translation state back to the workload.

The workload itself has not acquired a public IPv4 address.

External systems see the NAT egress address.

This makes NAT useful for:

  • outbound IPv4 connectivity
  • stable allow-listed egress identities
  • sharing public IPv4 addresses

It does not make NAT a firewall replacement.

Security groups, network ACLs, application authentication and destination controls remain relevant.

NAT is an address and connection-path mechanism.

It is not a certificate of privacy.

Private access does not require general internet egress

A workload in a private or isolated subnet may need to access:

  • Amazon S3
  • AWS APIs
  • another VPC
  • an internal platform service
  • a database
  • a private observability endpoint

None of those requirements automatically means:

send traffic through NAT

Interface VPC endpoints create private network interfaces in selected subnets. Workloads reach them using private VPC routing and DNS.

Gateway endpoints for Amazon S3 and DynamoDB add service-specific prefix-list routes to selected route tables.

For example:

Destination          Target

10.64.0.0/16         local
pl-s3-prefix-list     vpce-s3

The application can reach S3 without a general default route through NAT.

PRIVATE WORKLOAD
      │
      ▼
VPC ENDPOINT
      │
      ▼
AWS SERVICE

EKS clusters with limited or no internet access similarly need an explicit dependency plan for the AWS APIs, registries, package sources and telemetry destinations they use.

Private does not mean disconnected.

It means the paths are deliberate.

A private subnet can have a large blast radius

Imagine a subnet with no internet-gateway route.

It is labelled private.

Its route table contains:

10.0.0.0/8 → transit gateway

The transit gateway connects:

  • production
  • staging
  • shared services
  • observability
  • backup
  • corporate networks
  • partner networks

The subnet has no direct internet path.

It can still reach a substantial part of the organisation.

NO DIRECT INTERNET ROUTE

does not mean

SMALL NETWORK REACH

A broad private route can carry more organisational consequence than one carefully controlled public endpoint.

This is why subnet documentation should describe actual reachability:

subnet:
  class: private-application
  direct_internet_gateway_route: false

reachable_networks:
  - shared-services
  - observability
  - backup

internet_egress:
  path: central-egress
  inspection: required

inbound:
  sources:
    - public-alb-security-group

The word private remains useful shorthand.

The full contract explains what the subnet can actually reach.

Public and private IPv6 require two decisions

IPv6 often uses globally unique addresses without IPv4-style public-address translation.

Subnet classification still depends on routing.

For AWS public IPv6 space:

Direct public IPv6 route

::/0 → internet gateway

This creates a direct IPv6 path to the internet gateway.

Outbound-only IPv6 route

::/0 → egress-only internet gateway

An egress-only internet gateway allows resources to initiate outbound IPv6 communication while preventing internet hosts from initiating connections through that gateway.

PRIVATE DUAL-STACK SUBNET

IPv4:
0.0.0.0/0 → NAT or central egress

IPv6:
::/0 → egress-only internet gateway

This example assumes that the subnet uses public global-unicast IPv6 space.

AWS also supports private IPv6 space. AWS does not advertise that space publicly and drops it at the internet-gateway edge even when the VPC contains an internet gateway or egress-only internet gateway and a route points towards it.

The complete model is therefore:

ADDRESS TYPE
      +
ROUTE
      +
SECURITY POLICY
      =
ACTUAL REACHABILITY

AWS treats IPv4 and IPv6 routes independently.

Adding:

0.0.0.0/0

does not create:

::/0

Dual-stack architecture requires two deliberate path decisions.

The main route table can change subnet behaviour quietly

Every subnet must use one route table.

A subnet may be explicitly associated with a custom table or implicitly associated with the VPC’s main route table. One subnet can use only one subnet route table at a time, while one route table may serve several subnets.

Suppose a new data subnet is created without an explicit association.

It inherits the main route table:

10.64.0.0/16 → local

Later, someone adds:

0.0.0.0/0 → internet gateway

to the main route table.

Every implicitly associated subnet now receives that direct route.

ONE MAIN-TABLE CHANGE
        │
        ▼
MANY SUBNET ROUTING CHANGES

The resource names and diagrams may still say:

private
data
internal

The routing behaviour has changed underneath them.

For important subnet classes, I prefer explicit associations and a deliberately quiet main route table.

public-entry-a
    → public-entry-routes-a

application-a
    → application-routes-a

data-a
    → data-routes-a

transit-a
    → transit-routes-a

A subnet should not inherit a major network path because nobody remembered to mention the route table in the module.

Shared route tables create shared consequences

Several subnets may share one route table.

That is valid.

It also means every route change affects every associated subnet.

Suppose these subnet types share:

bfstore-private-routes
  • application nodes
  • databases
  • internal controllers
  • interface endpoints
  • observability agents

A new default route to Transit Gateway is added for application egress.

The database subnet receives it too.

SHARED ROUTE TABLE
       │
       ├── application subnet
       ├── database subnet
       ├── controller subnet
       └── endpoint subnet

The route may be harmless.

It may also broaden paths that were never intended for the data tier.

Subnet grouping should follow common routing requirements, not merely the word private.

Useful route-table classes may include:

private-application-egress

private-platform-egress

private-transit

isolated-data

isolated-control

A little more naming precision prevents a broad route table from becoming a communal coat that unrelated subnet types are forced to wear.

The bfstore edge pattern

For bfstore, the public application path should be narrow.

INTERNET
   │
   ▼
INTERNET GATEWAY
   │
   ▼
PUBLIC ENTRY SUBNETS
   │
   ▼
INTERNET-FACING ALB
   │
   ▼
PRIVATE APPLICATION SUBNETS
   │
   ▼
EKS PODS

The public subnets exist for components that need direct internet routing:

  • internet-facing load balancer nodes
  • zonal public NAT gateways, when that egress model is selected
  • selected edge appliances

They should not become general-purpose workload subnets.

Private application subnets can hold:

  • EKS nodes
  • application Pods
  • internal load-balancer targets
  • service network interfaces

Data subnets can hold:

  • RDS resources
  • data-tier interfaces
  • components requiring narrower connectivity

A database is not protected merely because its subnet name contains private.

Its route table, public-accessibility setting, security groups, authentication, encryption and operational access path must all agree with the intended boundary.

A public subnet should not become the automatic landing zone for administration either.

A bastion host with a public address can be made to work, but alternatives may include:

  • AWS Systems Manager Session Manager
  • private VPN access
  • Direct Connect
  • identity-aware access tooling
  • ephemeral administrative access

Public routing should exist because a component requires public routing, not because the component was inconvenient to reach privately.

Common misunderstandings

“A private subnet has no internet access”

Not necessarily.

It may use a zonal NAT gateway, Regional NAT Gateway, central egress or an egress-only internet gateway.

“A public subnet exposes everything inside it”

No.

Resources also require suitable public addressing, security controls and listening services for internet-initiated reachability.

“A public IP makes a subnet public”

No.

The route table determines the subnet type.

“A security group makes a subnet private”

No.

Security groups filter resource traffic. They do not select the network’s next hop.

“No internet route means isolated”

Not necessarily.

The subnet may still route to Transit Gateway, VPN, peering or other external networks. It may also reach interface endpoints through VPC-local routing.

“Private means trusted”

No.

Private describes the route path, not the identity or integrity of the source.

“Every NAT gateway belongs in a public subnet”

No.

A zonal public NAT gateway does. A Regional NAT Gateway does not require a public subnet.

“The subnet name proves its type”

No.

Inspect the actual route-table association and routes.

Questions I now ask

Area Core question
Routing Which route table applies, and where does each address family go?
Addressing Does the resource have public or private IPv4 or IPv6 identity?
Exposure Which listeners and security controls permit inbound traffic?
Egress Does traffic use zonal NAT, Regional NAT, egress-only gateway or central routing?
Reach Which internal and external networks are reachable?
Isolation Are there any routes beyond the VPC, including service-prefix routes?
Coupling Which other subnets share the route table?
Return path Can responses return through the correct routing domains?
Ownership Who may change the route table or association?
Evidence Can the intended classification be verified automatically from the routes?

These questions turn public and private from reassuring adjectives into testable network statements.

The mental model I am keeping

My earlier model was:

PUBLIC SUBNET

unsafe


PRIVATE SUBNET

safe

The stronger model is:

                           SUBNET
                              │
                              ▼
                    ASSOCIATED ROUTE TABLE
                              │
           ┌──────────────────┼──────────────────┐
           │                  │                  │
           ▼                  ▼                  ▼
     DIRECT IGW ROUTE     INDIRECT EGRESS    VPC-LOCAL ONLY
           │                  │                  │
           ▼                  ▼                  ▼
        PUBLIC             PRIVATE            ISOLATED
           │                  │                  │
           └──────────────────┼──────────────────┘
                              ▼
                     ADDRESSING CHOICES
                              │
                              ▼
                      SECURITY CONTROLS
                              │
                              ▼
                         APPLICATION
                              │
                              ▼
                    ACTUAL REACHABILITY

The route table answers:

Where may packets go next?

The direct internet-gateway route answers:

Is this subnet public
from a routing perspective?

NAT, egress-only gateways or central routing answer:

Can a private workload
initiate external communication?

Public addressing answers:

Does this resource possess
an internet-routable identity?

Security groups and network ACLs answer:

Which correctly routed traffic
may cross the relevant boundary?

The listener answers:

Is an application ready
to accept the connection?

Identity and authorisation answer:

What may the connected actor do?

And isolation answers:

Does the subnet have any route
to a destination beyond its VPC?

Public and private are useful terms.

They become dangerous when treated as security conclusions.

A public subnet is not necessarily exposed, and a private subnet is not necessarily isolated.

The public subnet may hold one carefully controlled load balancer and nothing else.

The private subnet may reach dozens of networks through Transit Gateway.

The isolated subnet may contain a database whose security group is still too broad.

The route tells me where the packet can travel.

The address tells me how the endpoint may be reached.

The security controls tell me which traffic may proceed.

The application tells me what happens after arrival.

Privacy is the outcome of all those layers agreeing.

The subnet label merely points towards the first one.

References and further reading