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:
- create isolated vpcs
- spin up subnets (public and private)
- set up nat so your private instances can reach the internet
- connect vpcs together with peering
- apply firewall policies
think of it as mini aws, but you can actually see what’s happening.
the stack:
- python (because i dont know bash and life is too short for c)
- linux network namespaces (for isolation)
- linux bridges (for virtual switches)
- veth pairs (for virtual cables)
- iptables (for firewalling and nat)
uvfor package management (because pip is slow and i guess twitter people think uv is cool now)
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
- it creates a linux bridge
- it enables ip forwarding and sets up routing policies
- it disables icmp redirects
- 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
- it creates a network namespace
- it tcreates a veth pair
- it plugs one end of the veth into the bridge and the other end into the namespace
- it sets up ip addresses and routing
- 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:
- network namespaces are incredibly powerful
- linux networking is actually very elegant once you understand it (granted i still dont fully)
- iptables are logical
- most cloud networking are mosre often than not just clever use of linix primitives (long live torvalds lol)
- testing networking code is painful (hence the 5 test scripts omo)
the final test suite:
- basic connectivity (subnets can talk to each other)
- vpc isolation (separate vpcs can’t communicate)
- vpc peering (except when you want them to)
- firewall policies (drop specific ports)
- multi-tier app deployment (web → app → database tiers)
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.