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
- Verify ownership of the domain with a TXT record.
- Point DNS at Freestyle.
- Map the domain to a VM port.
- 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 onlylocalhost.
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.