Ever wondered how Amazon, Google, or Microsoft assigns unique public IP addresses when using their services? Or you ever wanted to simulate your own mini Google Cloud!?!?! with subnets and custom IP addresses and CIDR and veth pairs and publicly accessible web servers in those subnets and like the entire internet?? all without typing 20+ commands? Because let’s face it, that’s some Houdini level stuff.

Meet vpcctl. A CLI tool built exactly for that! VPC creation? it’s got it. Subnets, say less. Firewall? BETβall with single commands!
No more 30 line bash scripts, or memorizing of iptables syntaxes.
Ready to build your personal cloud datacenter?
Getting Started
Prerequisites
- Linux host
- Root access or
sudoprivileges - Dependencies:
ip,iptables,jq,brctl,nginx
Why Should I Run as Root?
vpcctl configures and manipulates operations that require root privileges (network namespaces and iptables). While you can run commands with sudo, running as root is recommended for best results.
Quick Start
Clone the repo
git clone https://github.com/Clue-ess-coder/vpcctl ~/.vpcctlAdd
vpcctlbinary to PATHecho 'export PATH="$HOME/.vpcctl/bin:$PATH"' >> ~/.bashrc source ~/.bashrc
Verify Installation
# should show a beautiful help menu
vpcctl
(See walkthrough for a demo setup)
Wahh! It doesn’t work:
- Run
source ~/.bashrcor restart your shell - Ensure you’re root or using sudo
(See Troubleshooting for more.)
Design Philosophy
The tool is designed to use as little flags as possible (only 1 actually) to reduce the overhead needed to run commands. The syntax can take a little getting used to, but it ensures users think systematically about each intended action.
It also uses a configuration file (config.json) which acts as the single source of truth for all operations. This allows preview of all configuration/changes anytime (See Configuration).
High-Level Architecture

CLI Usage
When in doubt, simply run vpcctl (with or without --help) and the beautiful help page below should populate your terminal.
VPC Management
Run the following commands to create, remove, or list all VPCs.
vpcctl create vpc <name> <cidr>
vpcctl rm vpc <name>
vpcctl ls vpcs
Example:
vpcctl create vpc vpc1 10.10.0.0/16
vpcctl rm vpc vpc1
vpcctl ls vpcs
Subnet Management
Run these to create, remove, and list subnets in a VPC:
vpcctl create subnet <vpc> <subnet> <cidr> <type>
vpcctl rm subnet <vpc> <subnet>
vpcctl ls subnets <vpc>
Example:
vpcctl create subnet vpc1 pub 10.10.1.0/24 public
vpcctl create subnet vpc1 priv 10.10.2.0/24 private
vpcctl ls subnets vpc1
vpcctl rm subnet vpc1 pub
vpcctl rm subnet vpc1 priv
Peering
VPCs are isolated by default. Enable peering to allow traffic flow between them.
vpcctl create peering <vpc1> <vpc2>
vpcctl rm peering <vpc1> <vpc2>
Firewall
Apply robust firewall rules via JSON formatted policy files.
# Subnet level
vpcctl firewall <vpc> <subnet> <policy-file.json>
vpcctl firewall show <vpc> <subnet>
vpcctl firewall remove <vpc> <subnet>
# VPC level
vpcctl firewall vpc <vpc> <policy-file.json>
vpcctl firewall vpc remove <vpc>
Sample policy file:
{
"mode": "permissive",
"enable_logging": false,
"ingress": [
{
"protocol": "icmp",
"action": "allow",
"comment": "Allow all ICMP (ping)"
}
],
"egress": [
{
"port": 443,
"protocol": "tcp",
"action": "deny",
"comment": "Block outbound HTTPS"
},
{
"port": 22,
"protocol": "tcp",
"action": "deny",
"comment": "Block outbound SSH"
}
]
}
Find example configurations here
Deployment and Testing
Deploy and run tests on simple Nginx or Python servers natively in any subnet.
vpcctl deploy <vpc> <subnet> <port>
vpcctl get-ip <vpc> <subnet> [service]
vpcctl run <vpc> <subnet> <command>
vpcctl test <vpc> <subnet> <ip> <port> <protocol>
Examples:
## deploys nginx server in public subnet on port 8080
vpcctl deploy nginx vpc1 pub 8080 [--ip] 10.0.1.35
## pings google.com from public subnet in vpc1
vpcctl run vpc1 pub ping -c 4 8.8.8.8
## tcp/ping/icmp test on server listening on port 8080 in public subnet
vpcctl test vpc1 pub 10.0.1.10 8080 [tcp|ping|icmp]
Configuration and Logs
Show, set or get config details from a single config.json file.
vpcctl config get <path>
vpcctl config set <path> <value>
vpcctl config show
checks logs when needed:
vpcctl show-logs
Examples:
## prints all vpc
vpcctl config get '.vpcs'
## prints all subnets in vpc1
vpcctl config get '.vpcs["vpc1"].subnets'
## set automatic ip allocation to start from .20
vpcctl config set '.settings.ip_allocation.start_offset' 20
## print entire config
vpcctl config show
Demo Walkthrough
Modify environment variables:
cd ~/.vpcctl mv .env.example .env # edit variables vim .env --- VPC_NAME=vpc1 CIDR_BLOCK=10.0.0.0/16 PUBLIC_SUBNET=10.0.1.0/24 PRIVATE_SUBNET=10.0.2.0/24 PUBLIC_SUBNET_NAME=pub PRIVATE_SUBNET_NAME=priv INTERNET_INTERFACE=eth0Create VPC:
> vpcctl create vpc [vpc1 10.0.0.0/16] # optional manual input [2025-11-11 17:30:24] [INFO] Creating VPC: vpc1 with CIDR 10.0.0.0/16 [2025-11-11 17:30:24] [INFO] Creating bridge: br-vpc1 [2025-11-11 17:30:24] [INFO] Assigning gateway IP: 10.0.0.1/16 [2025-11-11 17:30:24] [INFO] VPC vpc1 created successfullyCreate a public subnet
# from environment variables > vpcctl create subnet "" "" "" public [2025-11-11 20:29:14] [INFO] Creating subnet: pub in VPC vpc1 [2025-11-11 20:29:14] [INFO] Creating namespace: ns-vpc1-pub [2025-11-11 20:29:14] [INFO] Creating veth pair: veth-pub-h <-> veth-pub-ns [2025-11-11 20:29:14] [INFO] Attaching veth-pub-h to bridge br-vpc1 [2025-11-11 20:29:14] [INFO] Moving veth-pub-ns into namespace ns-vpc1-pub [2025-11-11 20:29:14] [INFO] Configuring interface in namespace [2025-11-11 20:29:14] [INFO] Adding route to VPC CIDR 10.0.0.0/16 [2025-11-11 20:29:14] [INFO] Adding default route [2025-11-11 20:29:14] [INFO] Enabling NAT for public subnet [2025-11-11 20:29:14] [INFO] Enabling NAT for 10.0.1.0/24 via eth0 [2025-11-11 20:29:14] [INFO] NAT enabled for 10.0.1.0/24 [2025-11-11 20:29:14] [INFO] Adding route from host to public subnet 10.0.1.0/24 [2025-11-11 20:29:14] [INFO] Subnet pub created successfullyCreate a private subnet
> vpcctl create subnet "" "" "" private [2025-11-11 23:19:06] [INFO] Creating subnet: priv in VPC vpc1 [2025-11-11 23:19:06] [INFO] Creating namespace: ns-vpc1-priv [2025-11-11 23:19:06] [INFO] Creating veth pair: veth-priv-h <-> veth-priv-ns [2025-11-11 23:19:06] [INFO] Attaching veth-priv-h to bridge br-vpc1 [2025-11-11 23:19:06] [INFO] Moving veth-priv-ns into namespace ns-vpc1-priv [2025-11-11 23:19:06] [INFO] Configuring interface in namespace [2025-11-11 23:19:06] [INFO] Adding route to VPC CIDR 10.0.0.0/16 [2025-11-11 23:19:06] [INFO] Adding default route [2025-11-11 23:19:06] [INFO] Blocking host access to private subnet 10.0.2.0/24 [2025-11-11 23:19:07] [INFO] Host access to private subnet 10.0.2.0/24 is blocked [2025-11-11 23:19:07] [INFO] Subnet priv created successfullyDeploy nginx servers in subnets
# optional custom IP address flag > vpcctl deploy nginx vpc1 pub 8080 [--ip] 10.0.1.35 # ... deployment logs ... # Test: # curl http://10.0.1.35 > vpcctl deploy nginx vpc1 priv 8081 # automatic ip allocation (.10+) # ...more logs... # Test: # curl http://10.0.2.10:8081Verify accessibility of server in subnets from host
#### Public subnet > curl http://10.0.1.35 # should return: VPC Test Server VPC: vpc1 Subnet: pub IP: 10.0.1.35 Port: 8080 Server: nginx-1762903786 Nginx is serving this response! ### Private subnet curl http://10.0.2.10:8081 # ...NO RESPONSE (blocked as expected)Test communication within VPCs from either subnets
#### public -> private > vpcctl test vpc1 pub 10.0.2.10 8081 tcp [2025-11-11 23:53:42] [INFO] Testing connectivity from pub to 10.0.2.10:8081 (tcp) [2025-11-11 23:53:42] [INFO] TCP connection to 10.0.2.10:8081 successful #### private -> public > vpcctl test vpc1 priv 10.0.1.35 8080 ping [2025-11-11 23:57:24] [INFO] Testing connectivity from priv to 10.0.1.35:8080 (ping) [2025-11-11 23:57:26] [INFO] ICMP ping to 10.0.1.35 successfulTest outbound access from subnets
#### ping google > vpcctl run vpc1 pub ping -c 3 8.8.8.8 [2025-11-12 00:03:11] [INFO] Running command in subnet pub (namespace: ns-vpc1-pub) [2025-11-12 00:03:11] [INFO] Command: ping -c 3 8.8.8.8 PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data. 64 bytes from 8.8.8.8: icmp_seq=1 ttl=115 time=61.8 ms 64 bytes from 8.8.8.8: icmp_seq=2 ttl=115 time=88.0 ms 64 bytes from 8.8.8.8: icmp_seq=3 ttl=115 time=160 ms --- 8.8.8.8 ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 2003ms rtt min/avg/max/mdev = 61.782/103.233/159.892/41.471 ms ## Private subnet (blocked) > vpcctl run vpc1 priv ping -c 3 8.8.8.8 [2025-11-12 00:07:08] [INFO] Running command in subnet priv (namespace: ns-vpc1-priv) [2025-11-12 00:07:08] [INFO] Command: ping -c 3 8.8.8.8 PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data. --- 8.8.8.8 ping statistics --- 3 packets transmitted, 0 received, 100% packet loss, time 2025msNow run through these steps once more for another VPC and establish peering with the previously created VPC by running:
vpcctl create peering vpc1 vpc2Run more
ping,tcpandcurltests to verify services can now talk to each other:## TCP test from public subnet in vpc1 to an nginx server at 192.168.1.11:8080 (which could exist in vpc2 VPC and pub2 subnet under that VPC) vpcctl test vpc1 pub 192.168.1.11 8080 tcpRun
./test-firewall.shfor a robust firewall testRun individual cleanups with the
remove|rmcommands for each section (VPCs, subnets, peering, and firewall), or run a robust cleanup operation withvpcctl-cleanupAnd if you mistakenly ran the wrong commands or broke something (which you might do a few times), simply checks the logs in
.vpcctl/logs/
Sequence Diagrams
VPC and Subnet Creation

Service in Subnet β Internet (firewall enabled)

Inter-subnet Communication

VPC Peering

VPC Deletion

Firewall Application
sequenceDiagram
participant User
participant vpcctl
participant iptables
participant namespace
User ->> vpcctl: firewall my-vpc public policy-config.json
iptables -->> vpcctl: validate JSON
parse policy
iptables -->> vpcctl: validate rules
(ports, protocols)
vpcctl ->> namespace: flush existing rules in namespace
namespace -->> iptables: iptables -F INPUT/OUTPUT
vpcctl ->> namespace: set default policy
namespace -->> iptables: iptables -P INPUT/OUTPUT/FORWARD DROP
(or ACCEPT if permissive)
vpcctl ->> namespace: add stateful tracking
namespace -->> iptables: iptables -I INPUT 1 -m conntrack...
vpcctl ->> namespace: apply ingress, egress, forward rules
namespace -->> iptables: rules applied
vpcctl ->> namespace: add logging
(if enabled)
namespace -->> iptables: iptables -A INPUT -j LOG
iptables -->> vpcctl: save policy to config
vpcctl -->> User: firewall applied
Troubleshooting
“Command Not found”
# reload bashrc
source ~/.bashrc
“Permission denied”
# are you root?
$ whoami
---
root
# or use sudo
sudo vpcctl ls vpcs
“NAT Not Working or No Internet in Public subnet”
# check if IP forwarding is enabled
$ sysctl net.ipv4.ip_forward
---
# output should be
net.ipv4.ip_forward = 1
“Firewall Rules not Blocking traffic”
# check if rules were applied
vpcctl firewall show vpc1 pub
# enable logging in policy file
{"enable_logging": true}
# tail logs
sudo journalctl -f | grep FW-DROP
“Resources Still Exist”
# force cleanup of all namespaces
ip netns list | grep <vpc-name> | xargs -I {} ip netns delete {}
# remove all bridges
ip link delete br-<vpc-name>
# run cleanup again
vpcctl-cleanup
---
[2025-11-11 11:43:56] Starting vpcctl cleanup...
[2025-11-11 11:43:56] Stopping all running servers...
[2025-11-11 11:43:56] Killing process: 9907
[2025-11-11 11:43:56] Removing nginx server directories...
[2025-11-11 11:43:56] Removed nginx directories from /tmp
[2025-11-11 11:43:56] Removing nginx server files...
[2025-11-11 11:43:56] Removed nginx log files from /tmp
[2025-11-11 11:43:56] Cleaning up NAT rules...
[2025-11-11 11:43:56] Removing NAT rule for 10.0.1.0/24
[2025-11-11 11:43:56] Removing peering connections...
[2025-11-11 11:43:56] Removing all network namespaces...
[2025-11-11 11:43:56] Deleting namespace: ns-vpc1-pub
[2025-11-11 11:43:56] Deleting namespace: ns-vpc1-priv
[2025-11-11 11:43:56] Removing any orphaned veth interfaces...
[2025-11-11 11:43:56] Deleting orphaned veth: veth-priv-h@if124
[2025-11-11 11:43:56] Removing all VPC bridges...
[2025-11-11 11:43:56] Deleting bridge: br-vpc1
[2025-11-11 11:43:56] Removing any orphaned bridges...
[2025-11-11 11:43:56] Resetting config file...
[2025-11-11 11:43:56] Running final cleanup checks...
[2025-11-11 11:43:56] Cleanup complete!