runs-on.dev
Free subdomains for developers. Claim yourname.runs-on.dev in seconds,
then point it wherever you like.
Claim a name · Quickstart · Guides · Records · Policy
Claiming a name writes one file to this repository. That file is the record. It is the only thing that makes the name resolve, and you can read it without asking anyone.
{
"name": "yourname",
"owner": { "github": "yourname" },
"claimedAt": "2026-01-01T00:00:00.000Z",
"records": {}
}
There is no hidden database. The registry is this directory, the history is the git log, and
the rules are in lib/.
Why this exists
A real top-level domain means an ICANN application. The 2026 round's evaluation fee alone is $227,000, before you've built or run a registry to back it. That's not a plausible way to get a custom-looking address for a side project.
runs-on.dev gets the same feeling: a distinctive ending instead of
vercel.app or github.io, for the price of one domain, about $10 a
year, by giving away subdomains under it. This is a subdomain registry,
not a TLD. Every name you claim lives under runs-on.dev, which Advance
Labs registered and is responsible for. Saying that plainly, instead of
dressing it up as something bigger, is the whole basis for trusting it.
There's prior art doing exactly this: is-a.dev,
js.org, eu.org.
Claim a name
Go to runs-on.dev, sign in with GitHub, and type
the name you want. If it's available, claiming it writes a record to
domains/ in this repo, as shown above, and the name is live
within seconds. One name per GitHub account. See
docs/claiming.md for eligibility rules, the
per-account limit, and why they exist.
Point it at your own hosting
By default a claimed name serves a small profile card built from your GitHub account. To point it at your own site, forward email, or a plain redirect instead, there are two ways to change its record.
From the site. Sign in and open
runs-on.dev/manage. Pick a record type, fill
it in, and save. That writes a commit to domains/ in this
repository, exactly as a merged pull request would, and DNS follows within
seconds. The form offers the record types in the combinations DNS actually
permits, so it cannot build a record the schema would reject.
By pull request. Still supported, and the right choice when you want the change reviewed before it lands:
- Fork this repo.
- Edit
domains/, adding a record under.json records(and
subdomains, for a one-level-deep entry like _atproto).
- Open a pull request. CI validates the change against
schema/record.schema.json; once it's
green and merged, a workflow pushes the record to DNS automatically.
Both paths enforce the same rules, from the same code in
lib/edit.js: only the owner may change a record, and
owner, claimedAt, and name are immutable once set.
| Type | Shape | Coexistence |
| --- | --- | --- |
| CNAME | a hostname string | Alone: cannot sit next to A, TXT, or MX. |
| A | 1+ IPv4 addresses | With TXT, MX. |
| TXT | 1+ strings, up to 255 chars each | With A, MX. |
| MX | 1 to 5 { priority, value } entries | With A, TXT. |
| URL | one absolute http(s):// string | Alone: cannot sit next to anything, not allowed under subdomains. |
Nine hosting providers, a URL redirect, email forwarding, a Bluesky
handle, and Discord verification, each with the exact record to copy, are
in docs/guides.md. The same walkthroughs render on the
site at /docs/guides/ with more detail per provider; the
markdown file is the compact version for reading here.
Once a name serves your own site, docs/seo.md covers what
changes when that site lives on a subdomain: robots.txt and sitemaps are
per host rather than per domain, redirect names cannot rank, and a name
needs its own Search Console property.
Two worked examples
Vercel, the shortest path from claim to a live custom domain:
"records": { "CNAME": "<hash>.vercel-dns-017.com" }
Add you.runs-on.dev as a custom domain on the Vercel project first; the
Domains tab then shows the CNAME target to use. It is usually specific to
your project, so copy what it shows rather than the placeholder above.
A Bluesky handle, which needs no hosting at all, just a subdomain entry:
"subdomains": {
"_atproto": { "TXT": ["did=did:plc:abc123"] }
}
Full record reference, the subdomains grammar, and why CNAME can't
coexist with anything else: docs/records.md.
Built on runs-on.dev
Overlap — finds meeting times for people spread across time zones. Share one link, everyone sets their hours once, and the page shows when you can all meet, with sleep treated as a hard constraint rather than as free time. Open source, AGPL-3.0: zordhalo/overlap. Write-up: the first thing built on runs-on.dev.
It runs on a claimed name like any other, which is the point: domains/overlap.json
is an ordinary record, and the name resolved with a valid certificate before any
DNS was configured for it.
If you build something on a name from here, open a PR adding it to this list.
How it works
A single wildcard *.runs-on.dev DNS record points every possible
subdomain at one Vercel project, so an unclaimed or record-less name still
resolves with a valid HTTPS certificate and gets served the profile-card
page. Claiming a name is therefore a git commit, not a DNS write. Pointing
a name at your own hosting merges a record into domains/, and
a GitHub Actions workflow pushes that exact record to Vercel's DNS API on
merge, which then takes priority over the wildcard for that one name. More
detail, including the two write paths and the token split, in
docs/architecture.md.
The rules
Everything CI checks on a domains/** pull request, so you can verify a
PR yourself before opening it:
- One file per pull request.
- The path must match
domains/, matching.json
^domains/([a-z0-9-]+)\.json$.
- Renaming a record is refused outright. Claim a new name instead.
ownerandclaimedAtare immutable once set; only the recorded owner
- New names may be claimed either on the site or by pull request. Both
- An owner may delete their own record by pull request; a maintainer can
See lib/pr.js for the implementation CI actually runs,
and docs/contributing.md for the full breakdown
of what it checks.
Self-hosting
This is an open registry, not a hosted product with a private backend. Running your own copy under a domain you own means:
- Fork this repo and rename it;
REGISTRY_REPOin.env.examplepoints
- Register your own domain, point a wildcard record at your deployment,
scripts/sync-dns.mjs and lib/dns.js
for a different DNS provider).
- Change
ROOTinproxy.jsand the hostname literals inapp/page.jsx
app/sites/[name]/page.jsx from runs-on.dev to your domain.
- Create your own GitHub OAuth app and set
GITHUB_CLIENT_ID/
GITHUB_CLIENT_SECRET.
- Review
data/reserved-*.jsonand adjustlib/eligibility.jsfor your
Local development
npm install
npm run dev
The claim flow needs a signed-in GitHub session, and the session and
OAuth cookies are set Secure (see app/api/auth/github/route.js and
.../callback/route.js). A Secure cookie is dropped by the browser over
plain HTTP, so the GitHub sign-in flow cannot be exercised on
http://localhost. This is correct for production: runs-on.dev is on
the HSTS preload list, so it's always HTTPS there, and a cookie that only
ever travels over HTTPS shouldn't get a Secure-free code path just for
local convenience.
To exercise sign-in locally, serve the app over HTTPS with a locally-trusted certificate, mkcert being the simplest way:
mkcert -install
mkcert localhost
Then run next dev behind a TLS-terminating proxy pointed at it, and set
APP_ORIGIN to the https:// URL you're serving from so the OAuth
redirect URI matches. Everything that doesn't touch sign-in, name
validation, schema checks, the blocklist, the record UI, works over plain
http://localhost without any of this. See
.env.example for every variable the app reads.
Star the registry
Every name claimed here is one commit in this repo, authored under the claimant's own GitHub account. If you have a name, you're already in the log — a star keeps the registry easy for the next person to find.
Contributing
Bug fixes, blocklist additions, and doc improvements are welcome. See docs/contributing.md for how to add to the blocklists, run the tests, and what CI checks.
npm install
npm test
License
The code in this repository is AGPL-3.0 licensed. It is a network service, so section 13 applies: run a modified copy and let other people use it over a network, and you owe those users your source. Using the registry, or claiming a name, carries no such obligation — this binds people who fork and host the software, not people who point a subdomain at their site.
Up to commit 3492bf7 the code was MIT, and that grant still stands for the
code as it was then; see NOTICE.
The bundled profanity blocklist (data/reserved-words.json) is seeded from
LDNOOBW/List-of-Dirty-Naughty-Obscene-and-Otherwise-Bad-Words
(English list) and keeps its own
CC BY 4.0 license; see
data/README.md.
Credits
The idea of giving away free subdomains under one registered domain isn't new; credit to is-a.dev for the prior art, and to js.org and eu.org for more of it.
Built and operated by Advance Labs, which registered
runs-on.dev and is the party responsible for what runs under it. See
POLICY.md · All Advance Labs products.