---
title: "VPNs"
description: "Use WireGuard to connect your computer to a Freestyle VPC."
url: "/docs/vms/network/vpns"
---

VPNs let your computer join a Freestyle VPC with WireGuard. Create an ephemeral WireGuard session for a VPC, bring the generated config up with the WireGuard CLI, then connect to VMs by their private VPC IPs.


## Terms

- A VPN is a temporary network connection from your computer into a private network.
- WireGuard is the VPN tool Freestyle uses. It creates an encrypted tunnel between your computer and the VPC.
- A tunnel is the private path that carries VPC traffic from your computer to Freestyle.
- A peer is the other side of the WireGuard connection. Your computer is one peer; Freestyle is the other peer.
- A WireGuard config is the small file `wg-quick` uses to create the tunnel. It includes a private key, so treat it like a credential.


## Install WireGuard Tools

Install the WireGuard CLI on your computer:

```bash macOS
brew install wireguard-tools
```

```bash Ubuntu/Debian
sudo apt-get update
sudo apt-get install -y wireguard-tools
```

```bash Fedora
sudo dnf install -y wireguard-tools
```


## Create A VPN Session

Create a VPC, attach a VM, and bring the tunnel up with `wg-quick`:

```ts title="vpn.ts"
import { freestyle } from "freestyle";
import { spawn } from "node:child_process";
import { rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";

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

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

const connection = await vpc.wireguard.createEphemeral();
const configPath = join(tmpdir(), `wg-${connection.sessionId}.conf`);

await writeFile(configPath, connection.clientConfig, { mode: 0o600 });
await new Promise<void>((resolve, reject) => {
  const child = spawn("sudo", ["wg-quick", "up", configPath], {
    stdio: "inherit",
  });
  child.once("error", reject);
  child.once("exit", (code) =>
    code === 0 ? resolve() : reject(new Error(`wg-quick up failed with ${code}`)),
  );
});

// Keep the tunnel tied to this script: when the process exits from Ctrl-C or
// SIGTERM, bring WireGuard down and release the ephemeral Freestyle session.
const close = async (code = 0) => {
  await new Promise<void>((resolve) => {
    spawn("sudo", ["-n", "wg-quick", "down", configPath], {
      stdio: "ignore",
    }).once("exit", () => resolve());
  });
  await connection.close().catch(() => undefined);
  await rm(configPath, { force: true });
  process.exit(code);
};

process.once("SIGINT", () => void close(130));
process.once("SIGTERM", () => void close(143));

console.log(`Created VM ${vmId} in VPC ${vpcId}`);
console.log("Try: ping -c 3 192.168.10.10");
console.log("Press Ctrl-C to disconnect.");

await new Promise(() => {});
```

Run the script:

```bash
export FREESTYLE_API_KEY="your-api-key"
bun run vpn.ts
```

The generated WireGuard config contains a client private key. This script writes it to your OS temp directory, removes it on shutdown, and closes the ephemeral Freestyle VPN session when you press Ctrl-C.

While the script is running, your computer has a route to the VPC CIDR. Traffic to `192.168.10.10` goes through WireGuard; normal internet traffic keeps using your regular network.

Verify that WireGuard has a peer and has sent traffic:

```bash
sudo wg show
```

Connect to a VM by its VPC IP:

```bash
ping 192.168.10.10
```

When you are done, press Ctrl-C in the script terminal. The script runs `wg-quick down`, removes the temp config file, and closes the ephemeral Freestyle VPN session.


## Use A System Config Path

For repeated local testing, you can save `connection.clientConfig` as `freestyle-vpc.conf`, install it under `/etc/wireguard`, and refer to it by name:

```bash
sudo mkdir -p /etc/wireguard
sudo install -m 600 freestyle-vpc.conf /etc/wireguard/freestyle-vpc.conf
sudo wg-quick up freestyle-vpc
sudo wg show freestyle-vpc
sudo wg-quick down freestyle-vpc
```


## What The Config Contains

`vpc.wireguard.createEphemeral()` returns a standard WireGuard config in `clientConfig`:

```ini
[Interface]
Address = 100.96.0.2/32
PrivateKey = generated-client-private-key

[Peer]
PublicKey = freestyle-server-public-key
AllowedIPs = 192.168.10.0/24
Endpoint = vpn-endpoint.example.com:51820
PersistentKeepalive = 25
```

`AllowedIPs` is the VPC CIDR, so only VPC traffic is routed through the tunnel.


## Close The Session

Ephemeral sessions are meant to be short lived. Close the session from your app when the user disconnects:

```ts
await connection.close();
```

If your process exits before it can close the session, Freestyle eventually expires the VPN resources.
