# How to add a custom domain to a sandbox Run `runtime domain add app.example.com 3000`, set the TXT and CNAME records it prints, then `runtime domain verify`. **On Runtime a custom domain is included in the price: up to 50 per account, with HTTPS certificates from Let's Encrypt and no monthly fee** (pricing checked 25 September 2026, [network products](/docs/pricing#network-products)). You pay only for the sandbox behind the name, from $0.03125 an hour for 2 vCPU and 4 GiB while it waits. A paused sandbox is woken by the first visit, so a site that is used now and then costs running time only while it is used. ## Add the name ```bash no-run runtime domain add app.example.com 3000 # prints the DNS records to set runtime domain verify app.example.com # live once the TXT record matches ``` ```ts check import { Runtime } from "withruntime"; const runtime = new Runtime(); const sbx = await runtime.sandboxes.create({ funding: "paid" }); await sbx.spawn("python3 -m http.server 3000"); const domain = await runtime.domains.add({ hostname: "app.example.com", sandboxId: sbx.id, port: 3000, }); console.log(domain.records); // set these at your DNS provider await runtime.domains.verify("app.example.com"); ``` ```python check from withruntime import Runtime runtime = Runtime() sbx = runtime.sandboxes.create(funding="paid") sbx.spawn("python3 -m http.server 3000") domain = runtime.domains.add("app.example.com", sandbox_id=sbx.id, port=3000) print(domain["records"]) # set these at your DNS provider runtime.domains.verify("app.example.com") ``` An agent connected through MCP uses `runtime_domain_add` and `runtime_domain_verify`. ## The DNS records | Record | Name | Value | Why | | ------ | ------------------------------------ | ------------------------- | ------------------------- | | TXT | `_runtime-challenge.app.example.com` | `runtime-verify=` | Proves the name is yours | | CNAME | `app.example.com` | `domains.runtimehost.com` | Sends visitors to Runtime | A bare name such as `example.com` cannot hold a CNAME, so set an A record to the address the answer gives instead. The name goes live when the TXT record matches. The first visit fetches its certificate, which takes a few seconds. ## Options and limits | What | Detail | | --------------- | ------------------------------------------------------------- | | Price | Included, up to 50 domains per account | | New names | 20 added a day | | Certificates | Let's Encrypt, issued only for names whose TXT record matched | | Traffic | HTTPS to one port of one sandbox; WebSockets work | | Paused sandbox | Woken by a visit, as a preview is | | Move it | Run `add` again with the new sandbox or port | | Remove it | `runtime domain rm app.example.com` | | Who can add one | Paid accounts, serving a paid sandbox | ## Mistakes and how Runtime handles them - **Only the CNAME is set.** A CNAME that points at Runtime proves nothing, so the name stays pending until the TXT record matches. This is on purpose: a record someone forgot to delete cannot be used to take their name. - **The server listens on the wrong address.** It must listen on `0.0.0.0` or `localhost` inside the sandbox, and be started with `spawn`, which keeps it running. A process started by `exec` ends with its command. - **A trial account or a trial sandbox.** The answer is `payment_required` (402). Add credit and serve from a paid sandbox. - **Two accounts claim the same name.** The DNS owner wins: whoever proves the name later takes it over, and the earlier claim ends. Each claim has its own token. - **Too many at once.** Past 50 domains the answer is `quota_exceeded` (409); past 20 in a day it is `rate_limited` (429) ([errors](/docs/networking#errors)). Every answer carries an `X-Runtime-Report` header, and Runtime can take a reported name down; it then stays down for every account. ## A domain or a preview? A [preview](/docs/javascript#share-a-port) is the quick way to show one port: an address under `runtimehost.com`, private with a token unless you make it public. A custom domain is for a name people will type or bookmark, such as a customer's app built by an agent. To revoke preview links without changing anything else, see [rotate a preview token](/how-to/rotate-a-preview-token). For traffic that is not HTTP, such as Postgres or MQTT, [open a TCP port](/how-to/open-a-tcp-port) instead. ## Related - [Custom domains](/docs/networking#custom-domains) in the networking guide. - [Domains, TCP ports, addresses and the tunnel](/docs/security#domains-tcp-ports-addresses-and-the-tunnel): how ownership is proved. - [Preview agent-built apps](/use-cases/preview-agent-built-apps). - [Pause and resume a sandbox](/how-to/pause-and-resume-a-sandbox). Facts on this page were checked on 25 September 2026.