Manage Cloudflare DNS records from the terminal

By

List, add, update and delete Cloudflare DNS records with cf-domains, using a DNS token kept apart from the Registrar token.

A zone already sits in your Cloudflare account, and you want to read its records and change one of them from the terminal. The dashboard works. It is a poor place to repeat the same edit, or to hand the steps to a script you can read back.

Cloudflare Domains Toolkit includes cf-domains for that DNS work. Listing zones, listing records, creating, updating, and deleting one record after confirmation are the whole command set. Buying a domain is a different program, the Registrar MCP server, with its own token and its own approval step. That flow, including the $20 limit on the initial charge, is Buy a domain with an AI agent that has to ask first. This page does not register anything.

DNS is the background for what a record is. The Cloudflare products I actually use is the wider map of the account these zones live in.

What you need

You need Node.js 20 or later, a Cloudflare account, and a zone that is already on that account. cf-domains does not buy the zone. After purchase, download the source zip from your Polar receipt and unzip it. The DNS command lives in the cli directory, separate from the mcp directory.

npm install
npm link
cf-domains --help

Run those in cli. npm link puts cf-domains on your PATH. The help text, as printed in the domain-buying tutorial, lists the commands:

Commands:
  auth                                   Manage Cloudflare API authentication.
  zones                                  List accessible Cloudflare zones.
  records <domain>                       List DNS records for a domain.
  add [options] <domain>                 Create a DNS record.
  update [options] <domain> <record-id>  Update a DNS record.
  delete [options] <domain> <record-id>  Delete a DNS record.
  help [command]                         display help for command

Create a token that is not the Registrar token. The DNS token needs Zone Read and DNS Edit, limited to the zones you want this CLI to touch. The Registrar token cannot edit DNS, and the DNS token cannot buy domains. On a Mac, cf-domains auth login stores the DNS token in the Keychain. The service name is cloudflare.domains-cli. The Registrar server uses cloudflare.domain-registrar-mcp. Mixing those two is how a later command fails while login “succeeded” for the other tool.

The docs don’t print the login prompt, so I won’t invent it. Run login, then check what was stored:

cf-domains auth login
cf-domains auth status

auth status is the preflight. The same help text tells you to run it before you change records, and to run cf-domains records for the domain before you edit. You don’t need an MCP client for any of this.

List the zone you mean to edit

A record command takes a domain name and talks to Cloudflare as that token. If the token cannot see the zone, or the name is not a zone in the account, you are not editing the site you think you are. The docs put it as a missing zone: the token cannot access it, or the domain is not a zone in that account.

List first. The write commands are easier to undo when you already know the record ids that were there.

cf-domains zones
cf-domains records northstar.example

northstar.example is the docs’ stand-in. Replace it with a zone you own. zones is the list of zones this token can see. records is one zone.

The app page shows a fictional coffee-shop demo of those two commands. The zones table in that screenshot has these columns:

id  name  status

The records table has these:

id  type  name  content  ttl  proxied  comment

I’m not copying the ids out of the screenshot. Cloudflare ids are long, and a transcribed screenshot is a bad place to copy one from. Use the id your own records command prints. The fictional demo prints DNS record created: and then a row in that records shape when an add succeeds. Treat that line as the cue to copy the id, when your CLI prints it.

The ttl column is Cloudflare’s value for that record. The documented add command below does not take a TTL flag, so this tutorial never sends one. Leave the number Cloudflare already stored until you have a reason, read from your own records output, to change it in the dashboard.

How cf-domains changes one DNS recordThe DNS token is stored apart from the Registrar token. cf-domains lists zones and records, creates or updates one record, and asks before it deletes.addupdateDNS token inKeychaincf-domains zonescf-domains recordsCreate or change?New record and itsidSame id, new contentdelete asks first

Add a TXT record and keep its id

A TXT record under a name you are not already using is the docs’ first test, because it is not the address of your site or your mail. An A record or a MX record at the zone apex can take the site or mail offline. The docs say to save original values before you modify production DNS, since changes can interrupt websites or email.

Example 1: a TXT name you can delete

cf-domains add northstar.example --type TXT --name toolkit-check --content 'first test'

--type, --name and --content are the flags in the docs’ command. toolkit-check is the name they use. The content is the string first test. Then list again and copy the id of that row:

cf-domains records northstar.example

The fictional demo on the app page prints DNS record created: and a row that includes the new id. If your CLI does that, the id is in that row. If it doesn’t, it is in the records list you just ran. Either way, the next command needs the id from your account, not an id from this page.

A second add with the same name is another create. The docs don’t say the second call replaces the first, and they don’t show an error string for a duplicate. When you want the content to change, use update and the id. That is the next section.

The records table on the app page has columns for id, type, name, content, ttl, proxied and comment. Read ttl and proxied there after a create. This page has no documented flag that sets them, so the command you type stays at --type, --name and --content. If the row comes back proxied, or with a ttl you didn’t choose, that is the value Cloudflare stored for that create. Don’t send a comment, or a ttl, until cf-domains add --help on your own machine lists the flag. The help text captured for this page doesn’t.

Two TXT rows with the name toolkit-check is what a second add leaves behind. Resolvers can return both strings. update on the first id leaves one row and changes the string people look up. Copy the id from records before that second command. An id from the screenshot is someone else’s row, and it will not match your zone.

Update that record, or add a CNAME

update takes the domain and the record id. The docs change only the content:

cf-domains update northstar.example RECORD_ID --content 'second test'

Replace RECORD_ID. List again afterwards. The name and the type stay, and the content is second test. You did not create a second TXT. The id is the handle that makes the second run an edit.

Example 2: a CNAME, which the TXT command never used

The written docs show --type TXT only. The app page is the place that shows another type. Its fictional demo adds a CNAME, and the caption says the CLI adds a CNAME and asks before it deletes a record. The records table in that screenshot includes a CNAME row whose content is a hostname, which is what a CNAME points at.

Use the same three flags, with a hostname as the content, on a zone you own:

cf-domains add northstar.example --type CNAME --name www --content northstar.example

www is the name. northstar.example here is only an example target. Point it at a hostname you mean. The screenshot’s add line also includes --proxied. I’m not putting that flag in the command above, because the docs’ add example doesn’t pass it, and this page shouldn’t grow flags the help text we have doesn’t list. Look at the proxied column after records if you need to see how Cloudflare stored the row.

That CNAME is a different record from the TXT. It has its own id. Updating the TXT id does not change www.

Delete the record you added

Undo is delete with the id you copied. It removes that one record. It does not restore an older value you overwrote with update. If you updated a real MX or A record, deletion is the wrong undo. You would update it back to the content you saved before the edit. For the TXT and the CNAME you created above, delete is the cleanup.

cf-domains delete northstar.example RECORD_ID

Deletion asks for confirmation. The app page screenshot shows that question, and the record is removed only after it is confirmed. Decline it and you have not deleted anything. The domain-buying tutorial says delete skips that question when you pass --yes. Use that only when you have already read the id back from records and you mean that row.

cf-domains delete northstar.example RECORD_ID --yes

Run records once more. The id you deleted is absent from that list. The other rows stay. A second delete of an id that is already gone is not described in the docs, so list first and don’t retry a delete against a guess.

Nothing here bills a domain registration. The toolkit’s price does not include a domain, and the Registrar $20 cap is the other tool. DNS edits are changes in your Cloudflare account. What they cost you is a broken name if you edit the wrong row, which is why the id and the confirmation exist.

Stop when the zone is missing

A write against the wrong account is worse than a refused command. zones is the list of names this token can see. If the domain is absent, stop. Don’t retry add with a slightly different spelling and hope the API creates a zone. This CLI manages records in zones you already have.

Example 3: the Registrar token, or a zone it cannot see

The docs’ sentence for this failure is specific. A missing zone usually means the token cannot access it, or the domain is not a zone in that account. The Registrar token is a common way to get there, because login succeeded for a different Keychain service. cf-domains auth status talks about the DNS token. The Registrar MCP server reads cloudflare.domain-registrar-mcp. A token that can quote a domain purchase still cannot list northstar.example here.

cf-domains zones

Read the names. Your zone is either in that list or it is not. When it is not, create the DNS token again with Zone Read and DNS Edit, scoped to that zone, and run cf-domains auth login so cloudflare.domains-cli gets the new value. Then zones again. The second run of zones is a read. It does not create a duplicate zone, and it does not bill anything.

Only after the name appears do you go back to records and add. The TXT from example 1 is still the right first write.

If something looks wrong

If zones does not show the domain, the docs’ missing-zone case applies. The token cannot access it, or the name is not a zone in that account. A Registrar token in the DNS Keychain service is the usual mix-up. auth login again with a token that has Zone Read and DNS Edit, stored as cloudflare.domains-cli.

If add or update is refused and zones already shows the domain, check the token’s permissions against the docs. They ask for Zone Read and DNS Edit together. A read-only token is the wrong one for a write. The error text for that refusal is not printed in the docs, so match the permission list rather than a message you expected to see.

If you updated and the old content is still what the world resolves, you may be looking at a different id, or at a name you did not mean. records is the check. The id in the update command has to be the id of that row.

If delete removed a production record, the confirmation was answered yes, or you passed --yes. add can recreate a record you still know the type, name and content of. It will be a new id. An update you made to an existing row is not undone by deleting a different test record.

If the site or mail broke after an edit to the apex, you changed a record that was in use. The docs warn about that. Put back the content you saved, with update and the original id, if that row is still there.

If you expected a TTL flag on add and there isn’t one in the docs’ command, don’t invent a number to send. The ttl column is on the records table. This CLI’s documented create flags are --type, --name and --content.

If two TXT rows share the name toolkit-check, both add commands ran. update one id, and delete the other, after you read both ids from records.

Get Cloudflare Domains Toolkit

Search, quote and register Cloudflare domains through an MCP server with human approval. Includes a DNS CLI and full MIT source.

See Cloudflare Domains Toolkit → · Read the docs · $20 including tax, with full source code.

More tutorials for my apps →

Want me to talk about your product? You can sponsor this site.