Custom Domains
Bring your own domain to Meshr and publish mesh-internal services on
subdomains under it — app.example.com, grafana.example.com,
api.example.com — each backed by a peer on your mesh and served
over HTTPS through the Meshr tunnel.
Two steps, in order:
- Verify you own the domain — one TXT record, one time.
- Expose a service — create an Endpoint on a subdomain, add the per-subdomain CNAME it shows you.
Step 1 — Verify the domain
When you add a domain in Dashboard → Domains, Meshr generates a
one-time verification token and asks you to prove ownership with a
DNS TXT record. This happens once per domain.
Add this record at your DNS provider (Cloudflare, Route 53, Namecheap, etc.):
| Field | Value |
|---|---|
| Type | TXT |
| Name | _meshr-verify.<your-domain> |
| Content | meshr-verify=<token-shown-in-the-dialog> |
For example.com the record name is _meshr-verify.example.com. The
content is the full meshr-verify=… string from the verify dialog —
copy it verbatim, including the meshr-verify= prefix.
Once the record is live, click Try again in the dialog (or the Verify button on the domain row). Meshr looks up the TXT record and flips the domain to Verified.
New TXT records can take anywhere from a few minutes to 48 hours to propagate, depending on your provider and the record's TTL. If verification fails immediately after you add the record, wait a few minutes and try again.
You can check whether the record is visible from the public internet before clicking Verify:
dig +short TXT _meshr-verify.example.com
# should print: "meshr-verify=…"
If dig shows the record but Meshr still reports "TXT record not
found", the record is propagating unevenly across resolvers — give it
a few more minutes.
Common verification problems
- Record added on the wrong name. It must be
_meshr-verify.<domain>, not<domain>ormeshr-verify.<domain>. The leading underscore matters. - Provider appended the domain twice. Some DNS UIs auto-append the
zone, turning
_meshr-verify.example.cominto_meshr-verify.example.com.example.com. Enter just_meshr-verifyif your provider already scopes the record to the zone. - Quotes around the content. Enter the value without surrounding
quotes; the provider adds them. The stored record should be exactly
meshr-verify=<token>. - Wrong token. Each domain gets its own token. If you deleted and re-added the domain, the token changed — copy the current one from the dialog.
Step 2 — Expose a service
After the domain is verified, create an Endpoint on a subdomain
(e.g. app.example.com) and pick the mesh peer + port that should
serve it. Its detail page then shows a CNAME record to add:
| Field | Value |
|---|---|
| Type | CNAME |
| Name | the subdomain (e.g. app) |
| Target | tunnel.meshr.to |
Each subdomain gets its own Endpoint and its own CNAME — app and
grafana are independent, point both at the tunnel and Meshr routes
each to its configured backend. TLS is terminated at the tunnel edge,
so the service on your mesh peer doesn't need its own certificate.
Verification covers the whole domain — verify example.com once and
you can expose any number of subdomains under it. The apex (example.com
itself) can't be a CNAME due to DNS rules; publish on a subdomain
(www, app, …) instead.
How it fits together
You own example.com
│
▼
_meshr-verify.example.com (TXT) ← Step 1, once per domain
│ proves ownership
▼
app.example.com (CNAME → tunnel.meshr.to) ← Step 2, per subdomain
│ routes to
▼
a peer on your mesh, served over HTTPS
Removing a domain
Deleting a domain in the dashboard stops routing for every Endpoint
under it immediately. The DNS records you added stay at your provider
until you remove them — clean up the _meshr-verify TXT and any
subdomain CNAMEs once you're done.