Naming & project config
How Beamd picks a tunnel's subdomain, including the port default, --as and --from, the one-label rule, and a committed beamd.yaml file.
A tunnel's public name is a single subdomain label on your edge's base domain:
api → https://api.beam.example.com. Beamd gives you three ways to set it,
and a project file so you rarely set it by hand.
The subdomain label
By default the label is the port number: beamd open 3000 →
https://3000.beam.example.com. That's fine for a quick look, but real apps
want a stable name.
--as and --from
Set the label explicitly, or derive it from your project:
beamd open 3001 --as api # literal label → api.<base>
beamd open 3001 --from repo # derive it → <repo-name>.<base>--from sources:
| Source | Label becomes |
|---|---|
port (default) | the port number |
dir | the current folder name |
repo | the git repository name |
branch | the current git branch |
worktree | <branch>-<repo> in a linked git worktree on a non-default branch, else the repo name |
--as (a literal) wins if you pass both. A good default for branch-based
previews is --from branch; set it once as a naming default.
The one-label rule
The wildcard certificate is exactly one DNS label deep: *.<base> (flat)
or *.<slug>.<base> (namespaced). So a tunnel name must be a single RFC 1123
label: lowercase letters, digits, and hyphens. Encode structure with hyphens,
not dots:
- ✅
api,web-api,proj-ab12-web - ❌
a.b(nested labels don't get a cert and won't resolve)
This matters most for multi-app and worktree workflows: name a worktree's API
and web as proj-ab12-api and proj-ab12-web, not api.proj-ab12.
Hosted vs self-host. The hosted service folds your org into the label as a
hyphen suffix (api-acme.beamd.run), so every tunnel stays under one
*.beamd.run certificate. A self-hosted edge instead namespaces with a dotted
slug (api.turing.beam.example.com), backed by a per-slug
*.turing.beam.example.com cert. Either way, the name you choose is still a
single label.
Project config (beamd.yaml)
Commit a beamd.yaml at the repo root so the right edge, scope, and name come from
context. Then open and run need zero flags. Create it with beamd link, or
by hand:
# beamd.yaml
server: acme.com # which edge (account)
scope: acme # which org (hosted; omit for a self-hosted edge)
from: repo # how to name the tunnel (or `name: <literal>`)Beamd walks up from the current directory to find it. A gitignored
beamd.local.yaml overrides it for your machine, following the .env / .env.local
pattern. A beamd.yaml references an edge and scope, never a token, so it's
safe to commit. (More on server / scope in Accounts & scopes.)
Multiple apps: services
A repo with more than one server (say an API and a web app) lists them under
services:, mapping a name to its local port:
# beamd.yaml
server: acme.com
scope: acme
services:
api: 3000
web: 8080Now each gets its own URL by name. The service name becomes the label, and the
shared server + scope are reused:
beamd open api # → api-acme.acme.com (port 3000)
beamd open web # → web-acme.acme.com (port 8080)
beamd open # lists the services to pick from--as still overrides the label. beamd link --services api=3000,web=8080
writes the block for you.
Precedence
The label resolves in this order, first match wins:
1. --as <literal> # this command
2. --from <source> # this command
3. beamd.yaml name: / from: # this repo (committed)
4. your naming default # ~/.beamd/config, e.g. { from: branch }
5. the port number # fallback