Overview
An IP address identifies a network interface so packets can be routed to it. IPv4 addresses are 32 bits (203.0.113.10), about 4.3 billion in total, which ran out years ago. IPv6 addresses are 128 bits (2001:db8::10), a practically unlimited space. Addresses are grouped into networks written in CIDR notation, such as 10.0.0.0/16, where /16 means the first 16 bits identify the network.
In system design you mostly deal with private address ranges inside a VPC, subnets per availability zone, NAT for outbound internet access, and public or elastic IPs on load balancers. Planning address ranges early prevents painful overlaps when you later connect networks with peering or VPNs.
The network part of an address is like a zip code that gets mail to the right neighborhood; the host part is the house number. Private addresses are like apartment numbers inside a building: meaningful inside, but mail from outside goes to the building's main address (NAT).
When to use it
- Designing VPCs and subnets in the cloud.
- Configuring firewall rules and security groups by address range.
- Understanding NAT and why servers see a different client IP.
- Allow-listing partners or blocking abusive ranges.
Where it shows up in interviews
Recognize it when: public vs private subnets, multi-AZ, NAT.
- Design a secure three-tier web app on AWS
- Design VPC layout for microservices
Recognize it when: rate limiting or geo rules based on IP.
- Design a rate limiter
- Design fraud detection
Where it is used in real software
A VPC like 10.0.0.0/16 is split into public subnets (with an internet gateway) and private subnets (outbound through a NAT gateway) in each availability zone.
Mobile carriers put thousands of users behind one public IPv4 address, which is why IP-based rate limits can block innocent users.
Every pod gets its own IP from the cluster CIDR, and services get stable virtual IPs.
Key terms
- CIDR
- Network prefix notation: 10.0.1.0/24 means 256 addresses sharing the first 24 bits.
- Private ranges
- 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16: not routable on the internet.
- NAT
- Rewrites private source addresses to a public IP for outbound traffic.
- Subnet
- A sub-range of a network, often one per availability zone and tier.
- X-Forwarded-For
- Header proxies add so backends can learn the original client IP.
How it works, step by step
- 1Pick a VPC range that will not overlap
For example 10.20.0.0/16 (65,536 addresses) distinct from office and partner networks.
- 2Split into subnets
Public /24 subnets for load balancers, private /20 subnets for app servers, per availability zone.
- 3Route public subnets to an internet gateway
Load balancers get public IPs.
- 4Route private subnets through NAT
Servers can call external APIs but cannot be reached directly from the internet.
- 5Reserve room to grow
Leave unused ranges for future tiers, regions, or Kubernetes pod CIDRs.
CIDR sizes
Addresses = 2^(32 - prefix) for IPv4
| CIDR | Addresses | Typical use |
|---|---|---|
| /32 | 1 | A single host in a firewall rule |
| /28 | 16 | Small subnet (cloud providers reserve a few) |
| /24 | 256 | A typical subnet |
| /20 | 4,096 | App or pod subnet per AZ |
| /16 | 65,536 | A whole VPC |
NOWCIDR: /32 | Addresses: 1 | Typical use: A single host in a firewall rule
Each step of one in the prefix halves or doubles the size. Kubernetes clusters consume addresses quickly, so give them generous ranges.
Implementation
// Check whether an IPv4 address belongs to a CIDR blockfunction ipToInt(ip: string): number { return ip.split(".").reduce((acc, octet) => (acc << 8) + Number(octet), 0) >>> 0;} function inCidr(ip: string, cidr: string): boolean { const [base, bits] = cidr.split("/"); const mask = bits === "0" ? 0 : (~0 << (32 - Number(bits))) >>> 0; return (ipToInt(ip) & mask) === (ipToInt(base) & mask);} inCidr("10.20.3.7", "10.20.0.0/16"); // trueinCidr("192.168.1.5", "10.0.0.0/8"); // false // Behind a proxy, read the original client IP from a trusted headerfunction clientIp(headers: Record<string, string>, socketIp: string) { const forwarded = headers["x-forwarded-for"]; return forwarded ? forwarded.split(",")[0].trim() : socketIp; // only trust it from your own proxies}Complexity and performance
Exhausted; NAT is everywhere.
Enough for every device, with room to spare.
Bitmask comparison.
Trade-offs
NAT conserves IPv4 and hides internal hosts, but breaks end-to-end connectivity and makes the true client IP harder to know.
Big ranges avoid running out later but increase the chance of overlap when connecting networks.
Variants and related techniques
Fixed public IPs that survive instance replacement; useful for partner allow-lists.
One IP announced from many locations; routing delivers packets to the nearest.
Serve IPv4 and IPv6 at the same time during migration.
Common mistakes
- Overlapping VPC ranges.
Fix: Plan a company-wide address map before creating networks you may need to peer.
- Trusting X-Forwarded-For from anyone.
Fix: Clients can forge it; only trust the header added by your own load balancer.
- Rate limiting only by IP.
Fix: NAT and mobile carriers share IPs; prefer user or API-key limits.
Interview questions
How do servers in a private subnet reach the internet?
Through a NAT gateway in a public subnet: outbound connections are translated to the NAT's public IP, and responses return, but unsolicited inbound connections cannot reach the servers.
How many addresses are in a /22?
2^(32 - 22) = 1,024 addresses.
Practice problems
| Problem | Difficulty | What it trains |
|---|---|---|
| Compute addresses for /18, /23, /27 | Easy | CIDR math. |
| Design a VPC for a 3-tier app across 3 AZs | Medium | Subnets and routing. |
| Plan address ranges for 20 VPCs that must peer | Hard | Non-overlapping allocation. |