Why the network is the first design decision you cannot undo
Almost everything in a cloud account can be changed later. IP address ranges are the exception. Once a VPC exists with workloads, databases and peering attached, changing its CIDR means rebuilding. Teams discover this when a VPC runs out of addresses because Kubernetes pods took thousands of them, or when two networks they need to connect both use the same default range.
Networking is a core part of our ClickOps and cloud setup service, designed together with the account structure. If you run Kubernetes, the sizing decisions here matter even more; see our notes on Amazon EKS.
What the default VPC gets wrong
Every AWS account arrives with a default VPC: public subnets in each zone, auto-assigned public IPs and a route to the internet. It is convenient for a tutorial and poor for production. Resources launched into it are internet-adjacent by default, and the ranges are identical in every account and region, which guarantees overlap. We usually remove default VPCs from all regions in every account as part of the baseline, and build a deliberate network instead.
What good looks like
- One written IP plan covering every account, environment and region.
- At least two availability zones per tier, with identical layout in each.
- Only load balancers, NAT and bastion-style entry points in public subnets.
- Application workloads in private subnets, databases in isolated subnets with no internet route.
- Gateway endpoints for S3 and DynamoDB, and interface endpoints where traffic volume justifies them.
- Flow logs on, shipped to the log archive.
- The whole thing defined in code.
How it works in practice
Address planning
Pick a supernet for the company, say a /8 or /12 from the private ranges, and carve it up: a block per region, then a block per account, then a block per VPC. Keep spare blocks unassigned. Check for conflicts with office networks, VPNs and any partner who will connect. This takes an afternoon and saves months.
The tiers
A representative layout for one VPC across three zones:
| Tier | Purpose | Internet route |
|---|---|---|
| Public | Load balancers, NAT gateways | Via internet gateway |
| Private application | Containers, EC2, Lambda in VPC | Outbound via NAT only |
| Database | RDS, ElastiCache, internal data stores | None |
Smaller subnets for public and database tiers, larger for application tiers, since containers and pods consume many addresses.
Terraform
The community VPC module is a sensible base, though thin internal modules are often better once you know your pattern. A trimmed example:
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "~> 5.0"
name = "acme-prod"
cidr = "10.20.0.0/16"
azs = ["eu-west-1a", "eu-west-1b", "eu-west-1c"]
public_subnets = ["10.20.0.0/24", "10.20.1.0/24", "10.20.2.0/24"]
private_subnets = ["10.20.16.0/20", "10.20.32.0/20", "10.20.48.0/20"]
database_subnets = ["10.20.8.0/24", "10.20.9.0/24", "10.20.10.0/24"]
enable_nat_gateway = true
one_nat_gateway_per_az = true
enable_flow_log = true
}
The values are illustrative; ranges come from your IP plan. Version constraints are pinned and reviewed on upgrades.
Egress, endpoints and cost
NAT gateways charge by the hour and by data processed. Heavy traffic to S3 through NAT is a classic surprise bill. A gateway endpoint for S3 and DynamoDB routes that traffic privately at no charge. Interface endpoints for services like ECR, STS and CloudWatch cost per hour per zone, so they pay off only above certain traffic levels. We model this with your real usage rather than guessing, and link the findings to cost budgets. Our EKS cost calculator can help if Kubernetes is the main consumer.
Connecting accounts and sites
Peering is simple for two or three VPCs but does not scale, as it is not transitive. Transit Gateway centralises routing for many VPCs and VPNs at the cost of an hourly attachment fee and more design work. For hybrid links we plan Site-to-Site VPN first and Direct Connect only when bandwidth or latency demand it.
Security layers
Security groups are the primary control, referencing other groups rather than IP ranges where possible. Network ACLs are stateless and easy to get wrong, so we use them sparingly, mainly as a coarse backstop. Flow logs give visibility for investigations, and DNS design affects security too, which we cover in DNS setup.
Azure equivalent
Azure VNets follow the same logic with different mechanics. Subnets span a region’s zones, network security groups attach to subnets or interfaces, and user-defined routes steer traffic through a firewall in a hub network. Private Endpoints replace the role AWS interface endpoints play for PaaS services. We apply the same single IP plan and tiering discipline.
Decisions and trade-offs
Big VPC or many small ones? One large VPC per environment is easy to reason about. Many small VPCs isolate better but need more connectivity. Match it to your account structure.
IPv6. Dual-stack avoids some NAT cost and address exhaustion, but adds complexity in firewalls and tooling. We consider it case by case rather than by default.
Centralised egress inspection. A shared firewall gives one place for outbound policy, at real cost and latency. Reasonable for regulated environments, excessive for many startups.
When not to over-design. A single product with one VPC per environment does not need Transit Gateway or a firewall appliance.
Common failure modes
- Overlapping CIDRs discovered at the moment of connection.
- Subnets too small for container networking.
- One NAT for everything, then a zone outage takes down egress.
- Databases in public subnets because it made a quick test easier.
- Undocumented manual route changes that Terraform later wants to revert.
Delivery and handover
You receive the IP plan, a diagram, the Terraform module and a short note on how to add a VPC or subnet safely. We build and destroy it in a scratch account first, so the first production build is a repeat. If you have an existing console-built VPC, we review it and decide whether to import or replace it, using the approach in our IaC migration plan.
How to tell it is working
- No security group or route exposes a database to the internet.
- Adding a new environment uses the module and an unused CIDR block, with no overlap checks by hand.
- NAT and data transfer charges are understood and not growing unexpectedly.
- Flow logs answer the question “what talked to this?” in minutes.
To review your current network, book a consultation.