Freestyle Docs

Freestyle / Docs

VM Domains

Route HTTPS traffic from a free style.dev name or your own domain to services running inside Freestyle VMs.

VM domains route public HTTPS traffic from a hostname to a port inside a Freestyle VM. Create a VM first, then create a TLS ingress rule routing the domain to the VM port that should receive traffic.

A rule takes two kinds of name: a free subdomain of style.dev, or a domain of your own that you have verified.

Free style.dev Names

Any unused subdomain of style.dev is yours to take, with no verification, no DNS records, and no certificate of your own. Names are one label under style.dev and are taken by the first account to publish one.

await freestyle.tls.rules.create({
  action: "allow",
  domain: "my-app.style.dev", // any unused style.dev subdomain
  source: { public: true },
  destination: { vmId, port: 3000 },
});

The name is yours from then on, and freestyle.domains.list() returns it alongside the domains you verified.

Domain Flow

To serve a domain of your own:

  1. Verify ownership of the domain with a TXT record.
  2. Point DNS at Freestyle with a CNAME to beta-web.freestyle.sh.
  3. Delegate _acme-challenge to beta-dns.freestyle.sh so a certificate can be issued.
  4. Create a TLS rule routing the domain to a VM port.
  5. Run a service in the VM that listens on that port.

To serve every subdomain of a domain rather than named ones, see Wildcard Domains.

Create A VM And Route A Domain To It

Create the VM, start a service inside it, then route the public hostname to the service port with a TLS rule: the open Internet as the source, the VM port as the destination.

import { Freestyle } from "freestyle";

const freestyle = new Freestyle();

const domain = "my-app.style.dev"; // any unused style.dev subdomain, or a domain you've verified

const { vm, vmId } = await freestyle.vms.create({
  // Required: a VM reaches nothing it has not been allowed to.
  firewall: { rules: [{ action: "allow", source: {}, destination: { public: true } }] },
});

// 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.tls.rules.create({
  action: "allow",
  domain,
  source: { public: true },
  destination: { vmId, port: 3000 },
});

console.log(vmId);

Route A Domain To An Existing VM

const rule = await freestyle.tls.rules.create({
  action: "allow",
  domain: "app.example.com",
  source: { public: true },
  destination: { vmId: "your-vm-id", port: 3000 },
});

console.log(rule.id); // tls-…

See Inbound TLS for wildcard domains and other public protocols. Outbound TLS covers secret injection and VM-to-VM services by name. To authorize each public HTTP request before it reaches the VM, use Forward Auth.

Stop Routing A Domain

Delete the rule to stop routing traffic from the domain to the VM. The domain stays verified, so you can route it again later without re-verifying.

await freestyle.tls.rules.delete(rule.id);

Requirements

  • Your own domain must be verified before a rule can route it. A *.style.dev name needs no verification.
  • DNS must point at Freestyle before traffic reaches the VM. style.dev already does.
  • HTTPS is provisioned automatically, once _acme-challenge is delegated — issuance is proven over DNS, so without that record the domain resolves but cannot present a certificate. style.dev names are covered by the platform’s own wildcard certificate.
  • The service inside the VM must listen on the rule’s destination port.
  • For HTTP servers, listen on 0.0.0.0, not only localhost.
esc