Skip to main content
A VPC is a private network in one region, carved into subnets, with interfaces (NICs) allocated out of those subnets. Nothing in a VPC can reach the internet or be reached from it until you add a gateway and a route, which is deliberate: see gateways and routing. The service is regional. The endpoint carries the region: https://network.sa-saopaulo-1.basaltic.sh.

VPCs

Why the CIDR has to be private, and the one field you can only set at create.

Subnets

What “public” actually means, which addresses you don’t get, and what blocks a delete.

Interfaces

A NIC with its own identity, and what survives a detach from an instance.

Names and access

Which resources get name-based CRNs, and where a policy wildcard is wider than it looks.

VPCs

name is 1–63 characters, lowercase letters, digits and hyphens, and cannot start or end with a hyphen. It is unique per account and it lands in the VPC’s CRN. Creating a VPC also creates its main route table. Subnets land there unless you say otherwise.

The CIDR must be private

cidr_v4 has to sit inside 10.0.0.0/8, 172.16.0.0/12 or 192.168.0.0/16. The reason is not tidiness: addressing your instances out of routable space you do not own would blackhole that space for your own workloads, and for anyone else’s once the VPC routes anywhere. Public reachability comes from a floating IP or a gateway, never from the instance’s own IPv4 address.
The whole prefix has to fall inside one private block, not just its first address. 10.0.0.0/7 starts in RFC 1918 and spans out of it, so it is rejected.
name and cidr_v4 are immutable. PATCH /v1/vpcs/{vpc_id} takes description and tags and nothing else — there is no VPC resize.

IPv6 is a create-time decision

assign_ipv6_cidr delegates a globally-routable /60 from the region’s pool and returns it as the VPC’s cidr_v6. Subnets then carve /64s out of it.
This is the only way a VPC gets IPv6, and it only works at create. You cannot bring your own v6 prefix, and you cannot add one to an existing VPC — a v4-only VPC stays v4-only for its whole life. Decide before you create it.
If the region has no IPv6 pool the create fails with 400 and “IPv6 is not available in this region”. Check regions for what is enabled where. There is no address translation for IPv6 anywhere on the platform: an instance in a dual-stack subnet uses its own global address. That changes how outbound-only IPv6 is expressed — see egress-only gateways.

Deleting a VPC

The delete refuses in two cases, each naming what is holding it:
  • Subnets remain. “VPC has subnets; delete them first.”
  • An internet gateway is still attached. “VPC has an internet gateway attached; detach it first.”

Subnets

The CIDR has to sit fully inside the parent VPC’s CIDR of the same family, and must not overlap another subnet in the same VPC. name is unique per VPC. gateway_ip defaults to the first address after the network address (10.0.1.1 on a 10.0.1.0/24). You can supply your own, as long as it is inside the CIDR. cidr and gateway_ip are immutable. description, tags and route_table_id are not.

Public and private are routing, not a flag

There is no public boolean on a subnet. A subnet is public when the route table it uses has a 0.0.0.0/0 route pointing at an internet gateway, and private otherwise. The subnet’s route_table_id is what decides, so moving a subnet between the two postures is a PATCH that re-associates it with a different table:
Everything about which routes exist lives on the gateways and routing page.

Which addresses you actually get

Three addresses in an IPv4 subnet are unavailable: the network address, the broadcast address, and the gateway. A /24 leaves 253 usable — nothing is held back for DNS or instance metadata, because the metadata endpoint is the link-local 169.254.169.254 rather than an address inside your subnet. Auto-allocation takes the lowest free address above the gateway. If you supply ip_address on an interface it is checked against all of it: inside the CIDR, not the network or broadcast address, and not already taken — either by another interface or held by another resource, such as a load balancer’s VIP.

Dual-stack subnets

Pass cidr_v6 at create — a /64 inside the VPC’s cidr_v6. The VPC must have been created with assign_ipv6_cidr. Omit it for a v4-only subnet. cidr_v6 on the response is how a client tells the two apart: it is null on a v4-only subnet and set on a dual-stack one, alongside gateway_ip_v6. Interfaces in a dual-stack subnet get a /128 automatically.
A v6 address on the subnet and a /128 on the NIC do not make anything reachable. IPv6 reachability is decided entirely by the route table’s ::/0 route — see IPv6 reachability is the route.

Deleting a subnet

Three things block it, and the error names the one that did: The third one is the surprising one. A load balancer’s VIP is held inside your subnet without being an interface. The delete refuses rather than dropping the address underneath the resource that owns it.

Interfaces

An interface is a NIC: one MAC and one IPv4 address in a subnet, a /128 too if the subnet is dual-stack, plus its own security-group membership.
ip_address and mac are picked for you when omitted. name is unique per subnet, the IP is unique per subnet, and the MAC is unique. Subnet, IP and MAC are all immutable — only description and tags can be patched. An interface exists on its own. It is not a child of an instance, and it keeps its address, its MAC and its security groups whether or not anything is currently using it.

Attaching an interface to an instance

Attachment happens on the compute API, not here:
An interface you created brings its own address, MAC and security groups, so per-NIC overrides on that call are rejected rather than silently ignored. You can also pass subnet_id instead and let compute provision a NIC for you. The difference shows up at detach:
Detaching returns it to standalone. The interface, its address and its security-group membership all survive, ready to attach somewhere else.
DELETE /v1/interfaces/{interface_id} refuses while a floating IP is attached to the interface — detach the address first. It does not check whether an instance is currently using the NIC. Detach it at the instance before you delete it, or you remove the interface out from under a live binding.
The attached_to field on an interface is always null today. Do not read it to decide whether a NIC is in use — list the instance’s NICs on the compute API instead.

Names, CRNs and tag-conditioned access

Every resource here takes a tags map, and every one of them is authorized by CRN, so policies can name them precisely. VPCs, subnets, route tables, interfaces, security groups and all three gateway types get name-based CRNs, which is what makes a wildcard readable:
Routes and floating IPs have no name of their own, so their CRNs are keyed by id. A route additionally authorizes against its parent route table’s CRN and tags — the narrowest thing a policy can name — so you grant network:CreateRoute on a table rather than on *.
A CRN is account-scoped, but subnet names are unique per VPC and interface names per subnet. So subnet/web in a policy matches a subnet called web in every VPC in the account, not just the one you had in mind. When you need to fence a single VPC’s subnets, condition on a tag with basalt:ResourceTag/<key> instead of relying on the name.

Quotas

VPCs, subnets, interfaces, route tables, security groups and floating IPs are each capped by a regional quota against your organization. Public IPv4 is the one worth watching: floating IPs and NAT gateway addresses draw on the same allowance, so a NAT gateway costs you a floating IP’s worth of quota.

Next

Gateways and routing

Getting traffic in and out, and the four things a public instance needs.

Security groups

Stateful allow rules, the default-deny posture, and what the platform enforces regardless.