sable apathy


making a vpc from scratch

2025-11-12

building a vpc network controller from scratch (no aws)

yo.

so, i spent the last few days building something pretty cool. a virtual private cloud controller that runs on a linux kernel, no extra tools. no cloud provider. just me, a linux server, and networking concepts.

why did i do this?

i wanted to tell some lie about how ive always wanted to know how aws vpcs work under the hood and how ive always loved networking since i was 5 but that would all be a lie.

this was for a task at hng.

its still nice to learn the basics of networking though.

what i built:

vpcctl - a command-line tool that lets you:

think of it as mini aws, but you can actually see what’s happening.

the stack:

how it works:

the core idea is simple: use linux network namespaces as isolated network environments. each subnet gets its own namespace completely isolated from everything else.

when you attempt to create a vpc with my vpcctl, here are the things that happen:

uv run vpcctl create-vpc --name my-vpc --cidr 10.0.0.0/16
  1. it creates a linux bridge
  2. it enables ip forwarding and sets up routing policies
  3. it disables icmp redirects
  4. it enables proxy arp so different subnets can communicate

when you now add a subnet:

uv run vpcctl create-subnet --vpc my-vpc --name web --cidr 10.0.1.0/24 --type public
  1. it creates a network namespace
  2. it tcreates a veth pair
  3. it plugs one end of the veth into the bridge and the other end into the namespace
  4. it sets up ip addresses and routing
  5. finally, if it’s public, configures nat using iptables masquerade

the tricky parts:

here area some of the issues i faced while building

1. asymmetric routing

private subnets could reach public, but not public to private. the issue was that the bridge had the entire vpc cidr (10.0.0.0/16) assigned, so it thought all ips were local. what i did was only assign specific subnet gateway ips to the bridge.

2. reverse path filtering

linux was dropping return packets because they came from “the wrong interface.” so disabled rp_filter on all veth interfaces.

3. vpc isolation

initially, vpcs could reach each other through the host’s routing table. so i fixed by adding explicit drop rules in iptables between different vpc bridges.

4. peering veth names

interface names in linux have a 15-character limit. it took me way too long to find this info. fixed by truncating vpc names to 4 chars each.

the things i learned:

the final test suite:

all tests passing (well, almost - firewall test has a cleanup bug i’ll fix later).

would i use this in production?

hell no. but that’s not the point. the point is understanding. when you build something from scratch, you gain intuition that you can’t get from reading docs.

next time you create a vpc in aws, you’ll know exactly what’s happening behind the scenes. and that’s worth way more than a polished production tool.

try it yourself:

git hub will be here https://github.com/Pheonix0x01/vpcctl if it is no longer there then i have moved it to my main github https://github.com/DavidIfebueme

git clone https://github.com/Pheonix0x01/vpcctl
cd vpcctl
sudo uv run vpcctl create-vpc --name test --cidr 10.0.0.0/16

full code on github. it’s messy, it’s educational, and it actually works.