AWS Intermediate #1: EC2 and VPC Basics

11 min read

If the Basics 7 posts wrapped up the prep stage — accounts, regions, IAM, billing, CLI, security, CloudWatch — now we step into the stage where you actually launch something. The 7 Intermediate posts aim to thread together the components you meet most on AWS — EC2, VPC, S3, RDS, Route 53, ALB, CloudFront — through an operational lens.

The first stop is EC2 and VPC. They are the oldest, most fundamental tools in the cloud. EC2 is “one virtual computer,” VPC is “the network those computers live in” — they always travel together.

The big picture #

When you spin up a single EC2 from the console, here’s what’s actually being created behind the scenes.

The full stack of one EC2 instance
              ┌──────────────────────────────────┐
              │              VPC                 │
              │  10.0.0.0/16                     │
              │                                  │
              │   ┌──────────────────────────┐   │
              │   │   Subnet (Public)        │   │
              │   │   10.0.1.0/24            │   │
              │   │                          │   │
              │   │     ┌────────────┐       │   │
              │   │     │ EC2 Instance│      │   │
              │   │     │ AMI = OS    │       │   │
              │   │     │ EBS = Disk  │       │   │
              │   │     │ ENI = Network│      │   │
              │   │     └────────────┘       │   │
              │   └──────────┬───────────────┘   │
              │              │                   │
              │              ▼                   │
              │      Route Table                 │
              │              │                   │
              │              ▼                   │
              │      Internet Gateway (IGW)      │
              └──────────────┼───────────────────┘
                          Internet

Each of the components in this picture is what we’ll line up next.

EC2 — one virtual computer #

EC2 (Elastic Compute Cloud) is AWS’s virtual machine service. The thing called an instance in the console is one virtual Linux/Windows machine.

Instance type #

For EC2, the name itself is the spec.

Shape of an instance type
t3.micro
│  │  └── Size (nano / micro / small / medium / large / xlarge / 2xlarge / ...)
│  └───── Generation (1, 2, 3, 4, 5, 6, 7 ...)
└──────── Family

Common families:

FamilyNicknameUse
tBurstableWorkloads that idle then spike. Default for dev / side projects
mGeneral purposeBalanced CPU / memory. General web servers
cCompute optimizedCPU-heavy work (encoding, build servers)
rMemory optimizedMemory-heavy work (Redis, big caches)
i, dStorage optimizedWorkloads with large local NVMe / HDD (DBs, data pipelines)
g, pGPUMachine learning / graphics

You start almost everywhere with t3.micro / t3.small / t3.medium. The t family has a CPU credit model, so it’s cost-effective when average utilization is low. Once it goes to production, moving to m is the usual next step.

t3 vs t3a vs t4g. t3 is Intel, t3a is AMD (~10% cheaper), t4g is ARM (Graviton, ~20% cheaper and faster). For new workloads, start by checking compatibility with t4g.

AMI — the OS image #

AMI (Amazon Machine Image) is the OS image you launch an instance from. A snapshot of an OS like “Ubuntu 22.04” or “Amazon Linux 2023” plus pre-installed tools.

Common AMIs:

  • Amazon Linux 2023 — RHEL-family that AWS builds and maintains. Smoothest on EC2
  • Ubuntu LTS — the most familiar choice. 22.04 / 24.04
  • Debian — lighter than Ubuntu
  • Windows Server — license cost included
  • User-built AMI — snapshot a running instance into a new AMI (#2)

AMIs are per region. An AMI in Seoul can’t be used in Tokyo. There’s a copy command to move them.

EBS — the disk #

EBS (Elastic Block Store) is the block storage (= “virtual SSD”) attached to an EC2. Even after you terminate the instance, EBS lives on as a separate disk. Without EBS, an EC2 is an empty shell.

Volume types:

TypeCodeUse
General Purpose SSDgp3The default. Great cost/performance
General Purpose SSD (older)gp2Old default. New workloads use gp3
Provisioned IOPS SSDio2 / io2 Block ExpressDBs that need high IOPS
Throughput Optimized HDDst1Sequential reads of large files (big data)
Cold HDDsc1Rarely accessed backups

You’ll start almost everywhere with gp3 — 20% cheaper than gp2, with IOPS / throughput tunable independently.

Instance Store is yet another option. Physically attached NVMe — fast, but gone when the instance terminates. Cache / scratch only.

VPC — the virtual network #

VPC (Virtual Private Cloud) is your own private network inside AWS. The private IP space where resources like EC2, RDS, and Lambda live.

CIDR block #

The first thing you decide when creating a VPC is the CIDR block — the IP address range.

Common VPC CIDRs
10.0.0.0/16    # 10.0.0.0 ~ 10.0.255.255 (65,536 IPs)
172.16.0.0/16  # 172.16.0.0 ~ 172.16.255.255
192.168.0.0/16 # private IP space

A /16 gives 65,536 IPs. A /24 gives 256. The norm is to start with a /16 and slice it into subnets within.

Use only private IP space. Pick from the RFC 1918 private ranges (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16). Using a public IP as your VPC CIDR can break internet access to that IP.

Default VPC #

A new account starts with a Default VPC in every region. CIDR 172.31.0.0/16, one public subnet per AZ, and an IGW automatically attached.

The Default VPC is fine for learning / prototyping. For production, the convention is to go with a freshly built custom VPC. Default VPC has everything exposed to the internet, which is weak from a security standpoint.

Subnet — slices inside the VPC #

A subnet is a finer slice of a VPC. Each subnet belongs to one AZ, so for Multi-AZ operations you need to create subnets in multiple AZs.

Example subnet split inside a VPC
VPC 10.0.0.0/16
├── Public Subnet A   10.0.1.0/24  (AZ a) ── ALB, NAT, Bastion
├── Public Subnet B   10.0.2.0/24  (AZ b)
├── Private Subnet A  10.0.10.0/24 (AZ a) ── EC2 (app)
├── Private Subnet B  10.0.11.0/24 (AZ b)
└── Database Subnet A 10.0.20.0/24 (AZ a) ── RDS
    Database Subnet B 10.0.21.0/24 (AZ b)

Subnet type isn’t a property you set yourself; the route table decides it. There’s no “public” attribute — a subnet whose route table has a route to the IGW is what’s called public.

Public vs Private subnets #

Public SubnetPrivate Subnet
Internet in/outDirectOut only via NAT
Public IP auto-assignIf you turn the option onUsually off
Where they sitALB, NAT GW, BastionEC2 (app), Lambda
Routing0.0.0.0/0 to IGW0.0.0.0/0 to NAT GW

The operational pattern is app servers in Private, exposure handled by the ALB in Public. App servers don’t get direct internet IPs, which shrinks the attack surface.

Route Table — “where to send it” #

A route table defines where traffic from a subnet goes. One subnet has one route table attached.

A public subnet's route table
| Destination    | Target          |
| -------------- | --------------- |
| 10.0.0.0/16    | local           |  ← inside the VPC is always local
| 0.0.0.0/0      | igw-xxxxxxxx    |  ← everything else goes to the IGW (internet)
A private subnet's route table
| Destination    | Target          |
| -------------- | --------------- |
| 10.0.0.0/16    | local           |
| 0.0.0.0/0      | nat-xxxxxxxx    |  ← everything else goes through the NAT GW

The local route is added automatically and can’t be deleted. All subnets in the same VPC can always talk to each other (modulo SG / NACL).

IGW — Internet Gateway #

The IGW is a one-way gateway between the VPC and the internet. One per VPC (or none if you want a fully private VPC).

The IGW itself doesn’t route traffic — traffic flows only when you add the IGW as a target in the route table. That’s why you can have some subnets be public and others not within the same VPC even though they share the same IGW.

The IGW is free. Availability is managed automatically.

NAT Gateway — outbound from private #

If an EC2 in a private subnet needs to pull OS patches or call an external API, it needs internet access. But the internet must not get into that EC2. NAT (Network Address Translation) does that job.

NAT Gateway traffic flow
[Private EC2] ──out──▶ [NAT GW (Public Subnet)] ──out──▶ [IGW] ──▶ Internet
                                                ◀ in not allowed

NAT GW lives in a public subnet (it has to be reachable to the internet so it can talk to the IGW). When the private subnet’s route table targets the NAT GW, traffic from the private subnet flows out through NAT to the internet.

NAT GW vs NAT Instance #

In the old days, you’d run NAT on a single EC2 yourself (NAT Instance). Today, you almost always use the managed NAT Gateway.

NAT GatewayNAT Instance (legacy)
ManagementAWS-managedDIY
AvailabilityAuto per AZ (put one in each AZ for Multi-AZ)Single EC2
BandwidthUp to 100 GbpsDepends on EC2 size
CostPer hour + per GBEC2 cost only

NAT GW cost trap #

NAT Gateway charges for both per-hour and per-GB processed. With heavy traffic, the bill is bigger than expected. Saving tips:

  • Skipping a NAT GW per AZ and running one cuts cost but kills cross-AZ availability (one AZ outage breaks internet for privates in the other AZ too)
  • Route S3 / DynamoDB traffic via a Gateway VPC Endpoint to bypass NAT (free)
  • Services like ECR / Secrets Manager can also bypass NAT via Interface VPC Endpoints (per-hour cost but no per-GB)

Security Group vs NACL — the firewalls #

A VPC’s firewall works in two layers. Details land in #2 EC2 Operations; for now, just the picture:

How a packet reaches the EC2
Internet ──▶ NACL (per subnet) ──▶ Security Group (per instance) ──▶ EC2
Security GroupNACL
Applied atInstance (ENI)Subnet
Stateful✅ (auto-allow responses)
RulesAllow only (no Deny)Allow + Deny
How often you touch itDailyAlmost never

In production you mostly only touch SGs and leave the NACL at the default.

Launching one EC2 — aws cli #

Instead of clicking through the console, here’s a single CLI:

Launching an EC2
aws ec2 run-instances \
  --image-id ami-0f3a440bbcff3d043 \
  --instance-type t3.micro \
  --key-name my-key \
  --security-group-ids sg-0abc1234def567890 \
  --subnet-id subnet-0abc1234def567890 \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=my-server}]'

Every choice you’d make in the console maps to a flag, one for one. Once you’ve seen the CLI version, “what was the console asking me to pick” becomes clear. CLI / SDK setup is in Basics #4.

Common pitfalls #

1) “Why can’t my EC2 reach the internet?” #

Checklist:

  1. Does it have a public IP? (subnet’s “auto-assign public IP” or an EIP)
  2. Is the subnet public? (does the route table have 0.0.0.0/0 → igw-...)
  3. Is the SG allowing outbound? (default is allow all, but you may have closed it)
  4. Is the NACL allowing outbound? (default is allow all)
  5. Is the OS-level firewall (ufw, iptables) blocking it?

Usually 1) or 2). For a private subnet, check the NAT GW path.

2) VPC CIDR set too small #

You started with 10.0.0.0/24 (256 IPs) and ran out of subnet space later — VPC CIDRs are hard to change after creation (you can add a secondary CIDR). Be generous and use /16 from day one.

3) NAT Gateway showing up large on the bill #

Tens of dollars a month for NAT GW alone? Audit the traffic. If S3 / ECR calls dominate, look at VPC Endpoints to bypass NAT.

4) “I lost my key pair” #

Lose the key pair you SSH into your EC2 with, and the practical answer is to terminate and recreate. That’s why people are increasingly moving to SSM Session Manager (#2) — keys aren’t even needed.

5) RDS sitting alone in the wrong AZ #

EC2 in AZ a, RDS only in AZ b — it works, but you’ll incur cross-AZ data transfer costs. Either co-locate, or run RDS Multi-AZ (standby on the other side).

6) EBS outliving the instance #

When you terminate, check whether EBS auto-deletes (DeleteOnTermination). If an old option left it false, you’ll see a familiar incident: EBS volumes for terminated instances quietly billing for a month.

Wrap-up #

What we took home this time:

  • EC2 = one virtual computer. Type (t3.micro, etc.) decides spec, AMI provides OS, EBS provides disk
  • AMI is per region. Common picks: Amazon Linux 2023 / Ubuntu LTS
  • EBS default is gp3. Instance store is volatile (cache only)
  • VPC = a private network inside AWS. Start with /16, only private IP space
  • Subnets belong to one AZ. Public / Private isn’t a property — the route table decides
  • Route table: 0.0.0.0/0 to IGW = public, to NAT = private
  • IGW = VPC ↔ internet. NAT Gateway = outbound only from private
  • NAT GW is expensive — bypass with VPC Endpoints
  • Firewalls: SG (per instance, stateful) + NACL (per subnet, stateless), only SG day-to-day
  • Pitfalls — five-step internet check, VPC CIDR too small, NAT cost, lost key, cross-AZ traffic, leftover EBS

Next — EC2 Operations #

The picture of launching one EC2 is set. Now we move to handling that EC2 safely.

In #2 EC2 Operations — security group, key pair, SSM, we go through SG rule design, the limits of key pairs and the migration to SSM Session Manager, and how to bake AMIs — the daily tools of operations.

X