---
title: "VPCs"
description: "Create private networks for Freestyle VMs and attach VM network interfaces to them."
url: "/docs/vms/network/vpcs"
---

VPCs let Freestyle VMs communicate over private IP addresses. Use them when a group of VMs should share an internal network for databases, services, worker pools, or agent environments that should not depend on public VM domains.


## Terms

- A VPC is a private network for your VMs. VMs in the same VPC can talk to each other without using public domains.
- A private IP is an address that only works inside the VPC, like `192.168.10.10`.
- A CIDR is the address range for the VPC. `192.168.10.0/24` means addresses from `192.168.10.1` through `192.168.10.254` are available for VMs.
- A NIC is a network interface on a VM. Attaching a NIC to a VPC is how the VM joins the private network.


## Create A VPC

```ts
import { freestyle } from "freestyle";

const { vpcId, vpc } = await freestyle.vpc.create({
  name: "workspace-network",
  cidr: "192.168.10.0/24",
});

console.log(vpcId);
```

The VPC CIDR is the private address range available to VMs attached to the network. Pick a range that does not overlap with your home, office, or VPN network.


## Attach VMs

Attach a VM by passing a routed network interface in `vms.create`:

```ts
const { vm: apiVm } = await freestyle.vms.create({
  internet: "disabled",
  nics: [
    {
      default: true,
      vpc: vpcId,
      mode: "routed",
      ipv4: "192.168.10.10",
      routes: [{ cidr: "0.0.0.0/0", via: "192.168.10.1" }],
    },
  ],
});

const { vm: workerVm } = await freestyle.vms.create({
  nics: [
    {
      default: true,
      vpc: vpcId,
      mode: "routed",
      ipv4: "192.168.10.11",
    },
  ],
});
```

Use `default: true` when the VPC interface should be the VM's default route. Use `mode: "routed"` for VPC networking.

Set `internet: "disabled"` when the VM should not receive Freestyle's host-provided internet route. Use NIC `routes` to configure guest routes through another VM in the VPC, such as a router VM at `192.168.10.1`. If `internet` is omitted, VMs keep the existing host NAT internet behavior.


## Test Private Connectivity

VMs in the same VPC can reach each other by private IP:

```ts
const result = await apiVm.exec("ping -c 3 192.168.10.11");
console.log(result.stdout);
```

Run services on `0.0.0.0` inside the destination VM when other VMs should connect to them. Run it under systemd so it stays up across the VM's life:

```ts
const python = (await workerVm.exec("command -v python3")).stdout!.trim();

await workerVm.fs.writeTextFile(
  "/etc/systemd/system/http.service",
  `[Service]
ExecStart=${python} -m http.server 8080 --bind 0.0.0.0
Restart=always
[Install]
WantedBy=multi-user.target`,
);
await workerVm.exec("systemctl daemon-reload && systemctl enable --now http");
```

Then connect from another VM over the VPC address:

```ts
const response = await apiVm.exec("curl http://192.168.10.11:8080");
console.log(response.stdout);
```


## Connect From Your Computer

Use [VPNs](https://www.freestyle.sh/docs/vms/network/vpns) to create an ephemeral WireGuard connection from your computer into a VPC.


## Current Limits

- VPC VM interfaces use `mode: "routed"`.
- A VM can attach one routed VPC interface.
- Choose a VPC CIDR that does not overlap with networks your VM or VPN client already uses.
