Skip to content

Public DNS with Cloudflare

This is the one-time setup that makes *.by-managed.works names resolve. It's for whoever runs the managed.works control plane (the ui/ app) — not something an end user of the CLI does. Until it's set up, everything else still works; the app just skips publishing public DNS records.

You need a domain in Cloudflare (e.g. by-managed.works) and about five minutes.

1. Add the domain to Cloudflare

If it isn't already there: Cloudflare dashboard → Add a site → enter your domain → follow the steps to point your registrar's nameservers at Cloudflare. Wait until the zone shows Active.

2. Create a scoped API token

Dashboard → My Profile → API Tokens → Create Token → Create Custom Token:

  • Permissions: ZoneDNSEdit
  • Zone Resources: IncludeSpecific zone → your domain
  • Create the token and copy it (you only see it once).

3. Find the Zone ID

Open the domain in the dashboard → Overview → copy the Zone ID from the right-hand sidebar.

4. Set two environment variables on the control plane

bash
CLOUDFLARE_API_TOKEN=<the token from step 2>
CLOUDFLARE_ZONE_ID=<the Zone ID from step 3>

Set them wherever the ui/ app runs (your host's env, the platform's secrets, a .env). That's it — on the next managed apply, the app publishes an A record for every internet-facing service to the node's public (or reserved) IP, and removes records for services you delete.

How it fits together

  • The CLI decides which names exist and where they point — it writes them to state.public_dns_records on every apply (only for services whose port is from: public, only on a connected project).
  • The control plane (ui/app/lib/dns.ts, called from the state-sync route) pushes that set to Cloudflare, tagging each record with managed:<projectId> so it only ever touches records it created.
  • Names follow <service>.<project>.<account>.by-managed.works, where <account> is your org slug and <project> is the project name slugified.

Notes

  • Different domain? Set network.public_dns_zone in managed.yaml to the zone you host, and point CLOUDFLARE_ZONE_ID at that zone.
  • Wildcard / proxy: records are created un-proxied (grey cloud) with a short TTL so they track IP changes. If you want Cloudflare's proxy, enable it per record or adjust proxied in dns.ts.
  • Private services (databases) never get a public record — by design, a public name pointing at a firewalled port would be useless. Use the internal *.managed.internal names for those (no Cloudflare needed; see Networking & DNS).