---
title: "VM Domains"
description: "Route HTTPS traffic from custom domains to services running inside Freestyle VMs."
url: "/docs/vms/domains"
---

VM domains route public HTTPS traffic from a hostname you control to a port inside a Freestyle VM. Attach domains when creating a VM, or create a mapping later for an existing VM.


## Domain Flow

1. [Verify ownership](https://www.freestyle.sh/docs/vms/domain-verification) of the domain with a TXT record.
2. [Point DNS](https://www.freestyle.sh/docs/vms/domain-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 With A Domain

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

const domain = "app.example.com";

const serverJs = `
const http = require("http");
const port = 3000;

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

const spec = new VmSpec()
  .aptDeps("nodejs")
  .additionalFiles({
    "/root/server.js": { content: serverJs },
  })
  .systemdService({
    name: "node-server",
    mode: "service",
    exec: ["/usr/bin/node /root/server.js"],
  });

const { vmId, domains } = await freestyle.vms.create({
  spec,
  domains: [{ domain, vmPort: 3000 }],
});

console.log(vmId);
console.log(domains);
```

Each entry in `domains` maps a public hostname to a port inside the VM:

```ts
await freestyle.vms.create({
  domains: [
    { domain: "app.example.com", vmPort: 3000 },
    { domain: "admin.example.com", vmPort: 4000 },
  ],
});
```


## Map An Existing VM

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


## 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`.
