Cloud VPC Network Diagram

A typical three-tier web application on AWS runs in one VPC spread over two Availability Zones. This cloud network diagram shows the subnets, gateways, route tables and security groups that decide what can talk to what. A second view traces the actual packet paths in and out.


1. The Diagram

AWS VPC 10.0.0.0/16 Across Two Availability Zones Internet Internet Gateway ALB node sg-alb: 443 from any NAT Gateway a Elastic IP App EC2 (a) sg-app: 8080 from sg-alb RDS primary sg-db: 5432 from sg-app outbound only ALB node sg-alb: 443 from any NAT Gateway b Elastic IP App EC2 (b) sg-app: 8080 from sg-alb RDS standby sg-db: 5432 from sg-app outbound only sync replication Public route table 10.0.0.0/16 -> local 0.0.0.0/0 -> igw Private RT (AZ a) 10.0.0.0/16 -> local 0.0.0.0/0 -> nat-a Private RT (AZ b) 10.0.0.0/16 -> local 0.0.0.0/0 -> nat-b Data subnets use a route table with only the local route: no internet path at all. VPC 10.0.0.0/16 (us-east-1) Availability Zone us-east-1a Public subnet 10.0.1.0/24 Private app subnet 10.0.11.0/24 Private data subnet 10.0.21.0/24 Availability Zone us-east-1b Public subnet 10.0.2.0/24 Private app subnet 10.0.12.0/24 Private data subnet 10.0.22.0/24
  1. VPC and AZs. The outer box is the VPC with its /16 CIDR. Inside, two Availability Zones (separate data centers) each get an identical set of subnets so one AZ can fail.
  2. Public subnets. A subnet is public only because its route table sends 0.0.0.0/0 to the Internet Gateway. The load balancer nodes and NAT gateways live here.
  3. Private app subnets. Instances have no public IP. Their route table sends internet-bound traffic to the NAT gateway in the same AZ (dashed yellow), so they can download patches but cannot be reached from outside.
  4. Private data subnets. The database has only the local route. RDS keeps a synchronous standby in the second AZ.
  5. Security groups. The yellow-outlined boxes carry stateful security groups that reference each other: internet to ALB on 443, ALB to app on 8080, app to DB on 5432. Nothing else is open.

2. How to Read It

How to Read a Network Topology Diagram Device router / switch / host labelled box per device physical / L2 link solid line = cable or port uplink / trunk yellow = emphasis dashed boundary groups devices allowed traffic blocked / failure arrows only show flows zone / VLAN / subnet

Cloud diagrams nest boundaries: region, VPC, AZ, subnet. Colour the subnet borders by reachability (green public, blue private) and put the route tables on the page, because routing, not location, decides what is public.

A common mistake is drawing the NAT gateway in a private subnet or sharing one NAT gateway across both AZs; the second works but means an AZ outage also cuts the other AZ off from the internet. Reviewers should be able to trace every arrow to a route-table line and a security-group rule.

3. Variant: Packet Paths

Variant: Packet Paths In and Out Internet client IGW ALB public subnet App EC2 private subnet inbound HTTPS: allowed, SG chain alb -> app App EC2 private subnet NAT Gateway public subnet IGW Package repo internet outbound updates: source NATed to the Elastic IP; replies return the same way direct inbound fails: no route, no public IP

The same network drawn as flows rather than containers. Inbound HTTPS reaches only the load balancer; outbound traffic from private instances leaves through NAT; a direct connection to a private instance fails because there is neither a public IP nor a route.

4. Pros, Cons, and When to Use

Pros

Cons

When to use

When not to use

5. As Diagrams-as-Code

Python diagrams library (mingrammer), which draws with the official AWS icons:

from diagrams import Cluster, Diagram
from diagrams.aws.network import ALB, InternetGateway, NATGateway
from diagrams.aws.compute import EC2
from diagrams.aws.database import RDS

with Diagram("VPC 10.0.0.0/16", direction="TB"):
    igw = InternetGateway("IGW")
    with Cluster("VPC 10.0.0.0/16"):
        for az in ("a", "b"):
            with Cluster(f"AZ us-east-1{az}"):
                with Cluster("public"):
                    alb, nat = ALB(f"ALB {az}"), NATGateway(f"NAT {az}")
                with Cluster("private app"):
                    app = EC2(f"app {az}")
                with Cluster("private data"):
                    db = RDS("primary" if az == "a" else "standby")
                igw >> alb >> app >> db
                app >> nat >> igw