> ## Documentation Index
> Fetch the complete documentation index at: https://docs.basaltic.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# VPCs

> Create a VPC with IPv4, public or private IPv6, subnets, and internet access.

<Tabs>
  <Tab title="Console">
    Go to **Networking → VPCs** and choose **Create VPC**. Under **VPC
    details**, give it a **Name** and an **IPv4 CIDR**.

    The page is a workflow rather than a single create. **Subnets** asks how
    many **Public subnets** and **Private subnets** to carve out of that CIDR
    and lets you edit each generated **Name** and **IPv4 CIDR**. When IPv6 is
    enabled, every planned subnet also has an **IPv6 CIDR** field: automatic
    for public GUA, editable for private ULA.

    **Internet access** offers an **Internet gateway**, a **NAT gateway**, and
    an **Egress-only gateway** when IPv6 and private subnets are selected.
    The egress-only option requires GUA. It creates the gateway and a `::/0`
    route in the private subnets' default route table, even when the plan has
    no public subnets. With NAT and egress-only both selected, IPv4 uses NAT
    and IPv6 uses native outbound-only routing. With NAT alone, both enabled
    families use NAT.
    Submitting hands you to a **Provisioning progress** page that walks the
    plan one step at a time — **Create VPC**, **Create public route table**,
    **Create internet gateway**, **Attach internet gateway**, **Configure
    public internet route**, then one step per subnet, and **Create NAT
    gateway** with **Configure private internet route** if you asked for NAT.

    <Warning>
      That sequence runs in your browser, which is why the page says **Keep
      this page open until provisioning finishes.** Navigate away mid-run and
      whatever was already created stays, but the remaining steps never
      happen. Come back to the same progress page and it offers **Retry
      failed step**, picking up where it stopped.
    </Warning>
  </Tab>

  <Tab title="API">
    ```bash theme={null}
    POST https://network.sa-saopaulo-1.basaltic.sh/v1/vpcs
    {
      "name": "prod",
      "cidr_ipv4": "10.0.0.0/16"
    }
    ```

    One call, one VPC. The subnets, gateway and routes the console workflow
    adds are separate calls you make yourself — see
    [gateways](/networking/gateways) and [routing](/networking/routing).
  </Tab>

  <Tab title="CLI">
    ```bash theme={null}
    basaltic network vpc create --name prod --cidr-ipv4 10.0.0.0/16
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    v, err := network.New(cfg).CreateVPC(ctx, &network.VPCCreateRequest{
        Name:   "prod",
        CIDRIPv4: "10.0.0.0/16",
    })
    ```
  </Tab>
</Tabs>

`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 default `<vpc-name>-private-rt` route table.
Subnets use it unless you select another table.

## The CIDR must be private

`cidr_ipv4` 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](/networking/floating-ips) or a [gateway](/networking/gateways), never from the instance's
own IPv4 address.

<Warning>
  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.
</Warning>

`name` and `cidr_ipv4` are immutable. `PATCH /v1/vpcs/{vpc_id}` takes
`description`, `tags`, and an initial IPv6 allocation. Existing CIDRs cannot be resized.

## IPv6 allocation

<Tabs>
  <Tab title="Console">
    On **Create VPC**, under **VPC details**, set **IPv6** to **Automatic public
    IPv6 (GUA)** or **Manual private IPv6 (ULA)**.

    GUA allocates a VPC `/60` and a distinct `/64` for each planned subnet.
    Each subnet's **IPv6 CIDR** shows **Automatically allocated /64**; the
    actual range is assigned during creation. A `/60` holds sixteen `/64`s.

    For ULA, **VPC IPv6 CIDR** starts with a generated private `/48`, and each
    subnet's **IPv6 CIDR** starts with a distinct `/64` from that range. Keep
    the suggestions or edit them. Every subnet must have an aligned `/64`
    inside the VPC range, and the ranges must not overlap.

    Changing the VPC IPv6 CIDR regenerates the subnet IPv6 ranges. Changing
    subnet counts or choosing **Regenerate** resets both families' subnet
    ranges to their suggestions. The form validates them before provisioning.
    ULA internet egress needs NAT; the egress-only option is disabled.

    You can also enable IPv6 later from the VPC detail page.
  </Tab>

  <Tab title="API">
    ```bash theme={null}
    POST /v1/vpcs
    { "name": "prod", "cidr_ipv4": "10.0.0.0/16", "allocate_cidr_ipv6": true }
    ```
  </Tab>

  <Tab title="CLI">
    ```bash theme={null}
    basaltic network vpc create --name prod --cidr-ipv4 10.0.0.0/16 \
      --allocate-cidr-ipv6
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    v, err := network.New(cfg).CreateVPC(ctx, &network.VPCCreateRequest{
        Name:           "prod",
        CIDRIPv4:         "10.0.0.0/16",
        AllocateCIDRIPv6: basaltic.Bool(true),
    })
    ```
  </Tab>
</Tabs>

`allocate_cidr_ipv6` delegates a globally-routable `/60` from the region's pool
and returns it as the VPC's `cidr_ipv6`. Subnets then carve `/64`s out of it.
The VPC wizard enables IPv6 on all its planned subnets.
Independently created subnets can remain IPv4-only, which is
useful when migrating workloads in stages or deliberately keeping a subnet
on one address family. A subnet's interfaces always inherit every family
enabled on that subnet.

Alternatively, supply a private ULA prefix in `cidr_ipv6`: a canonical prefix
inside `fd00::/8`, from `/48` through `/60`. Do not send it together with
`allocate_cidr_ipv6`. Choose a randomly generated ULA prefix to reduce collision
risk if networks are connected later.

An existing IPv4-only VPC accepts either option through
`PATCH /v1/vpcs/{vpc_id}`. Once assigned, the IPv6 CIDR cannot be replaced or
removed. Existing subnets remain unchanged until IPv6 is enabled on them.

If the region has no public IPv6 pool, automatic public allocation fails.
ULA addressing does not become internet-routable by adding an internet route.
Use a NAT gateway for shared outbound access, or a public IPv6 floating IP
when a ULA-addressed NIC needs its own public identity.
A NIC with a native global address can use that address directly when routing
and security rules allow it; no floating IP is required.

## Routed IPv4 pools

A VPC can reserve up to four IPv4 prefix pools inside `cidr_ipv4`. Pools must
not overlap subnets or one another. Manage them through
`/v1/vpcs/{vpc_id}/prefix-pools`. Each pool has an ID and `cidr_ipv4`.

Interfaces allocate `/28` prefixes from these pools through
`/v1/interfaces/{interface_id}/prefixes`, using `pool_id`. A NIC can hold up to
16 routed prefixes, returned separately as `routed_prefixes`. Prefixes route
to that NIC; they are not individual guest address entries or DHCP leases.
The guest configures workloads and routing for the delegated range. This is a
networking building block, not a managed Kubernetes service.

## 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."
