Freestyle Docs

Freestyle / Docs

VM Domains

Route HTTPS traffic from custom domains to services running inside Freestyle VMs.

VM domains route public HTTPS traffic from a hostname you control to a port inside a Freestyle VM. Create a VM first, then create a domain mapping for the VM port that should receive traffic.

Domain Flow

  1. Verify ownership of the domain with a TXT record.
  2. Point DNS at Freestyle.
  3. Map the domain to a VM port.
  4. Run a service in the VM that listens on that port.

Create A VM And Map A Domain

Create the VM, start a service inside it, then map the public hostname to the service port.

import { freestyle } from "freestyle";

const domain = "app.example.com";

const { vm, vmId } = await freestyle.vms.create();

// Write a small HTTP server into the VM.
await vm.fs.writeTextFile(
  "/root/server.js",
  `
const http = require("http");

http
  .createServer((_req, res) => {
    res.writeHead(200, { "Content-Type": "text/html" });
    res.end("<h1>Hello from a Freestyle VM</h1>");
  })
  .listen(3000, "0.0.0.0");
`,
);

// Install Node and run the server under systemd, so it stays up and restarts.
await vm.exec("apt-get update && apt-get install -y nodejs");
const node = (await vm.exec("command -v node")).stdout!.trim();

await vm.fs.writeTextFile(
  "/etc/systemd/system/app.service",
  `[Service]
ExecStart=${node} /root/server.js
Restart=always
[Install]
WantedBy=multi-user.target`,
);
await vm.exec("systemctl daemon-reload && systemctl enable --now app");

await freestyle.domains.mappings.create({
  domain,
  vmId,
  vmPort: 3000,
});

console.log(vmId);

Map An Existing VM

await freestyle.domains.mappings.create({
  domain: "app.example.com",
  vmId: "your-vm-id",
  vmPort: 3000,
});

Unmap A Domain

Remove a mapping to stop routing traffic from the domain to the VM. The domain stays verified, so you can map it again later without re-verifying.

await freestyle.domains.mappings.delete({
  domain: "app.example.com",
});

Requirements

  • The domain must be verified before it can be mapped.
  • DNS must point at Freestyle before traffic reaches the VM.
  • HTTPS is provisioned automatically.
  • The service inside the VM must listen on the mapped vmPort.
  • For HTTP servers, listen on 0.0.0.0, not only localhost.

Preview Domains

Every account can use *.style.dev for free as preview domains. Pick any unused <name>.style.dev subdomain and map it straight to a VM — a preview domain needs no DNS and no verification (none of the Requirements above apply), and HTTPS is provisioned automatically. They’re ideal for previews, demos, and testing:

await freestyle.domains.mappings.create({
  domain: `preview-${crypto.randomUUID().slice(0, 8)}.style.dev`,
  vmId,
  vmPort: 8080,
});

Bring your own domain when you’re ready for production; a *.style.dev preview domain is the zero-setup option in the meantime.

esc