Subnetting and IP Addressing Footguns¶
Mistakes that eat IPs, break routing, and create conflicts that surface weeks later.
1. Off-by-two: forgetting network and broadcast addresses¶
You plan for 254 hosts on a /24. You actually get 254 usable. But on a /26, you plan for 64 and only get 62. On a /28, you plan for 16 and get 14. The smaller the subnet, the higher the percentage lost to network and broadcast addresses. On AWS, it's worse — 5 reserved per subnet, so a /28 gives you 11, not 14.
Fix: Always subtract 2 (or 5 on AWS) from total IPs. Use ipcalc to verify before committing to a design.
Gotcha: AWS reserves 5 IPs per subnet: network address, VPC router (.1), DNS server (.2), future use (.3), and broadcast. A /28 on AWS gives you 11 usable IPs, not 14. Azure reserves 5 as well. GCP reserves 4. Always check your cloud provider's reservation policy before sizing subnets.
2. /31 and /32 confusion¶
A /32 is a single host — used in routing table entries and loopback addresses, not for subnets. A /31 is two IPs with no broadcast — used for point-to-point links between routers (RFC 3021). Assigning a server to a /31 or /32 when you meant /24 means it can't talk to anything on the local subnet.
Fix: /32 is for host routes and loopback. /31 is for router-to-router links only. Servers go on /24 or wider.
3. Overlapping CIDRs between VPCs or VPNs¶
You create VPC-A as 10.0.0.0/16 and VPC-B as 10.0.0.0/16. They peer fine until traffic for 10.0.1.50 could go to either VPC. Or your on-prem network uses 10.0.0.0/8 and your cloud VPC uses 10.0.0.0/16 — the VPC range is inside the on-prem range. VPN routes collide.
Fix: Plan all VPC and on-prem CIDRs in a single spreadsheet before building anything. Use non-overlapping /16 blocks within the 10.0.0.0/8 space (10.100.0.0/16, 10.101.0.0/16, etc.).
4. "I thought 10.0.0.0/8 was just one subnet"¶
Someone sees 10.0.0.0/8 and thinks it's a single flat network with 16 million hosts. They assign 10.0.1.x to dev, 10.0.2.x to prod, and assume everything can reach everything. Then they add firewall rules, VPC boundaries, or routing policies and discover that /8 is an address space, not a subnet. The actual subnets are whatever you carve out of it.
Fix: /8 defines the address space. You still need to subnet it into /16s, /24s, etc., with proper routing between them.
5. IPv6: forgetting link-local is always there¶
Every IPv6 interface has a fe80::/10 link-local address — always, automatically, even if you did not configure IPv6. Applications binding to :: (all interfaces) will listen on IPv6. Firewall rules that only cover IPv4 leave IPv6 wide open.
Fix: Audit ip -6 addr show on every host. Apply ip6tables rules or use nftables (handles both stacks). Never assume "we don't use IPv6" means it is not present.
Default trap: Every modern Linux kernel enables IPv6 by default. Applications binding to
::listen on both IPv4 and IPv6. Your carefully crafted iptables rules protect IPv4 but leave IPv6 wide open. Either apply matching ip6tables rules or use nftables which handles both address families in one ruleset.
6. Docker default bridge colliding with corporate networks¶
Docker's default bridge is 172.17.0.0/16. Docker Compose creates networks starting at 172.18.0.0/16, 172.19.0.0/16, etc. Many corporate networks use 172.16-31.x (the RFC 1918 172.16.0.0/12 range). Container traffic to internal services silently routes to the Docker bridge instead of the corporate network.
Fix: Override Docker's address pool in /etc/docker/daemon.json to use ranges outside your corporate space. Move Docker into 10.200.0.0/16 or a similarly unused range.
War story: This is one of the most common "it works on my machine" networking bugs. Developer runs
docker-compose up, Docker creates a 172.18.0.0/16 bridge, and suddenly the developer cannot reach internal services on 172.18.x.x because packets route to the Docker bridge instead of the corporate network. The fix is a one-time daemon.json change, but the diagnosis wastes hours.
7. Not reserving enough IPs for growth¶
You give a subnet /28 (14 usable IPs) because "we only have 8 servers today." Six months later, autoscaling kicks in and there are no IPs left. Pods fail to schedule, instances fail to launch, and the fix requires re-IPing the entire subnet.
Fix: Size subnets for 3-5x current need. A /24 is cheap. IP space in private ranges is free — do not be stingy with it.
8. Confusing CIDR prefix length with subnet mask octets¶
Someone says "we need a /255.255.255.0 subnet." They mean /24. Or they say "/16" but write the mask as 255.255.0.255. The prefix length is the bit count from the left; the dotted-decimal mask is its visual representation. Mixing them up produces invalid configurations that silently mis-route traffic.
Fix: Pick one notation and be consistent. In modern infra (Terraform, cloud consoles, K8s), CIDR notation (/24) is standard. Know the conversion: /24 = 255.255.255.0, /16 = 255.255.0.0, /8 = 255.0.0.0. Use ipcalc if unsure.