VPC (Virtual Private Cloud)
A VPC is an isolated virtual network inside a cloud account, with its own address range, subnets, and routing rules. What it holds and why builds run in one.
A VPC (virtual private cloud) is an isolated virtual network inside a cloud account, with its own private IP address range, subnets that divide that range, and routing rules that decide which traffic stays inside and which leaves. Every instance a cloud provider launches for you sits in one, and the VPC configuration decides what that instance can reach and what can reach it.
For a GitHub Actions workload the practical consequence is reachability. A build step that installs from an internal package registry or runs migrations against a private database needs a machine inside the network where those endpoints live, and the VPC is that network.
Definition
Each major cloud provider ships the same construct under its own name.
- AWS calls it a VPC and defines it as a virtual network dedicated to your AWS account, logically isolated from other virtual networks in the cloud (What is Amazon VPC, checked on 2026-08-13).
- Google Cloud calls it a VPC network: a virtual version of a physical network implemented inside Google's production network (VPC network overview, checked on 2026-08-13).
- Azure calls it a virtual network, or VNet, and describes it as the fundamental building block for a private network in Azure (Azure Virtual Network overview, checked on 2026-08-13).
The names differ and the parts are the same everywhere.
| Part | What it decides | AWS resource |
|---|---|---|
| Address range | The pool of private IP addresses available to everything inside | VPC IPv4 CIDR block |
| Subnets | How the range is split, and which zone a machine lands in | Subnet |
| Route tables | Where traffic for a given destination is sent | Route table |
| Gateways | Whether and how traffic reaches the public internet | Internet gateway, NAT device |
| Private service paths | Whether provider service traffic stays off the public internet | A private route to AWS services configured on the VPC |
| Packet filters | Which flows are allowed between machines and out of the network | Security group, network ACL |
The address range constrains everything downstream
A VPC is created with a private IPv4 CIDR block. On AWS that block can be between /16 and /28, and every subnet is carved from it. AWS also reserves five addresses in each subnet CIDR: the first four and the last.
| CIDR | Total addresses | Usable in an AWS subnet |
|---|---|---|
/16 | 65,536 | 65,531 |
/20 | 4,096 | 4,091 |
/24 | 256 | 251 |
/26 | 64 | 59 |
/28 | 16 | 11 |
Two limits follow from that arithmetic. A fleet of short lived machines consumes an address per running machine, so a subnet sized at /26 supports 59 concurrent instances and no more, whatever the compute quota says. And the range has to avoid overlapping with any network it is later peered or tunneled to, because overlapping ranges cannot be routed between. AWS documents default quotas of 5 VPCs per Region and 200 subnets per VPC, both adjustable on request (Amazon VPC quotas, checked on 2026-08-13).
Scope: what a VPC spans
Scope differs by provider and it changes how the network is laid out.
| Provider | VPC scope | Subnet scope |
|---|---|---|
| AWS | One Region | One Availability Zone |
| Google Cloud | Global, spanning all regions | One region |
| Azure | One region | Within the VNet address space, spanning zones |
On AWS the zone binding is the part that shapes a build fleet. Capacity for a given instance type is per zone, so a fleet restricted to one subnet is restricted to one zone's capacity. Spreading subnets across three Availability Zones spreads that risk.
Public subnets and private subnets
The distinction is a routing property rather than a subnet setting. A subnet whose route table sends 0.0.0.0/0 to an internet gateway is public, and machines in it can hold public IP addresses. A subnet whose default route points at a NAT device is private: outbound connections work, and nothing on the public internet can open a connection inward.
Private placement has a metered cost, because NAT devices are billed by the hour and by the gigabyte processed. In US East (N. Virginia) AWS charges $0.045 per hour per device plus $0.045 per GB processed in each direction (Amazon VPC pricing, checked on 2026-08-13). Configuring S3 access directly on the VPC keeps that traffic off the metered path.
Example
Take an existing VPC in us-east-1 with the range 10.20.0.0/16, three private subnets holding the build machines and three public subnets holding the NAT devices.
| Subnet | CIDR | Availability Zone | Default route | What runs there |
|---|---|---|---|---|
private-a | 10.20.0.0/20 | us-east-1a | NAT device in public-a | Build machines |
private-b | 10.20.16.0/20 | us-east-1b | NAT device in public-b | Build machines |
private-c | 10.20.32.0/20 | us-east-1c | NAT device in public-c | Build machines |
public-a | 10.20.240.0/24 | us-east-1a | Internet gateway | NAT device, load balancers |
public-b | 10.20.241.0/24 | us-east-1b | Internet gateway | NAT device, load balancers |
public-c | 10.20.242.0/24 | us-east-1c | Internet gateway | NAT device, load balancers |
An internal package registry and a staging database already run in this VPC and resolve through a private hosted zone. A self hosted runner launched into private-a is inside the same address range, so the job below reaches both over the VPC's local route with no public exposure of either service.
name: integration
on:
push:
branches: [main]
jobs:
test:
runs-on: [self-hosted, linux, x64]
steps:
- uses: actions/checkout@v4
- name: Install from the internal registry
run: npm ci --registry https://npm.internal.example.com
- name: Apply migrations against the staging database
run: psql "postgres://db.internal.example.com:5432/app" -f migrations.sql
- name: Show where this machine sits
run: |
TOKEN=$(curl -sX PUT http://169.254.169.254/latest/api/token \
-H "X-aws-ec2-metadata-token-ttl-seconds: 60")
curl -s -H "X-aws-ec2-metadata-token: $TOKEN" \
http://169.254.169.254/latest/meta-data/local-ipv4The final step prints a 10.20.x.x address, which is the machine's place in the range above. Each destination the job touches takes a different path out of the subnet.
| Destination in the job | Path | Leaves the VPC |
|---|---|---|
npm.internal.example.com at 10.20.x.x | Local route inside the VPC | No |
db.internal.example.com at 10.20.x.x | Local route inside the VPC | No |
github.com and api.github.com | Default route to the NAT device, then the internet gateway | Yes, and the bytes are metered |
| An S3 bucket in the same Region | A direct route to S3 when the VPC is configured for it, otherwise the NAT path | No when the direct route is configured |
Run the same job on a machine outside this VPC and the first two steps fail at DNS resolution, because neither hostname exists in public DNS and neither address is routable from outside. The usual workarounds all widen the network surface: publishing the registry, allowlisting a changing set of source addresses, or proxying through a bastion host. Placing the machine in the subnet removes the question, and the security group on that machine becomes the control that decides what the build is allowed to open.
Related Terms
- Subnets and the address block each one carves out of a VPC: how a range is divided, and why the split decides zone placement and concurrency headroom.
- Running GitHub Actions runners inside your own VPC: the network shape a build fleet needs, the outbound allowlist, and who pays for what.
- Networking requirements for BYOC runners on AWS: subnet layout, route tables, and endpoint configuration for a runner stack in your own account.
- What is Amazon VPC: the AWS definition, the components, and the default VPC behavior.
- VPC and subnet requirements for an AWS stack: the public and private subnet layout, NAT device placement, and address headroom to plan for.
- Runner networking documentation: connecting a runner to a private network for the length of one job.
- WarpBuild pricing: per minute rates by runner type.
FAQ
What is a VPC in plain terms?
A VPC is a private network you own inside a cloud provider's account. It has an IP address range you choose, subnets that carve that range into smaller blocks, route tables that decide where traffic goes, and gateways that decide whether traffic can leave. Every instance the provider launches for you sits in one.
What is the difference between a VPC and a subnet?
The VPC holds the whole address range and the routing configuration. A subnet is one block of that range with its own route table, and on AWS a subnet lives in exactly one Availability Zone. Machines are placed in subnets rather than in the VPC directly, so subnet choice decides the zone and the default route a machine gets.
Why would build machines run inside a VPC?
Because the services a build step needs often have no public address. A machine in the same VPC reaches an internal package registry or a database endpoint directly over the private range, which leaves both services unpublished and removes the proxy that would otherwise sit in front of them. Outbound access to the public internet is then a separate routing decision.
Start with $10 in free credits
Change the runner label in your workflow and keep the rest of your GitHub Actions setup. Runner time is billed per minute.