A Practical Guide to Cloudflare's New cf CLI
Get started with Cloudflare cf CLI: install and authenticate, manage API resources, configure and deploy Workers, and migrate projects from Wrangler safely.
cf is Cloudflare’s command-line tool for the entire Cloudflare API. It launched in open beta on 28 September 2026, and you install it with npm i -g cf on Node.js 22.18 or later.
If you deploy Workers with Wrangler, you probably still open the dashboard for DNS, zones and settings that Wrangler never covered. cf covers both: your account resources and your Worker projects, from one binary. This guide takes you through installing it, reading its output, writing the new TypeScript config, deploying, and moving an existing Worker across. If you need a refresher on Workers first, start with this beginner’s guide to Cloudflare Workers.
Key Takeaways
- Install cf with
npm i -g cfon Node.js 22.18 or later. Bun is not supported, and commands that load cloudflare.config.ts fail under it. - cf keeps its own credentials, so run
cf auth logineven if Wrangler is already signed in. - Most commands print their results as JSON on stdout, and status messages go to stderr, so you can pipe output straight into jq.
- Run
cf migratebeforecf dev,cf buildorcf deployin any project that has a wrangler.toml, wrangler.json or wrangler.jsonc. - Once the beta ends, Cloudflare plans a final major Wrangler release that points to cf, followed by 18 months of maintenance.
How Does cf Differ from Wrangler?
cf replaces a CLI that grew one command at a time with one generated from Cloudflare’s OpenAPI schemas. The launch announcement puts cf at more than 3,000 API operations, against roughly 280 in Wrangler. The cf docs count it differently: more than 2,900 commands. Cloudflare built cf with coding agents in mind, but every command works the same way when you type it yourself.
| Wrangler | cf | |
|---|---|---|
| Coverage (launch post figures) | ~280 operations | 3,000+ operations |
| Config | wrangler.toml / wrangler.json / wrangler.jsonc | cloudflare.config.ts |
| Build | Wrangler’s bundler (esbuild) | Vite by default, or Wrangler’s bundler for migrated projects |
| Login | wrangler login | Separate cf auth login |
| Status | Maintained for 18 months after cf’s beta ends | Open beta |
The changelog entry warns that commands, config and Build Output may still change before the stable release.
How Do You Install cf and Log In?
Install the cf package from npm, then authenticate with cf auth login. Other tools also install a command called cf, so make sure you have the official package, which links to the cloudflare/cf repository.
npm i -g cf
cf --version
cf auth login # add --no-browser over SSH or in a container
cf auth whoami
According to the Get started docs, the package gives you the same CLI under two names, cf and cloudflare. If something else on your PATH already answers to cf, call it as cloudflare instead. To log in, cf shows a link and a one-time code, then opens that link in your browser for you to approve. cf stores its own credentials, so a Wrangler login won’t carry over.
In CI, set CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID instead. A token set this way wins over any saved login.
Start with a read-only command:
cf zones list
How Do You Read and Filter cf’s JSON Output?
Most cf commands write their results to stdout as JSON, while progress and status messages go to stderr. So you can save results to a file or pipe them into jq with no extra flags.
cf zones list > zones.json # results only; status stays on stderr
cf zones list | jq . # inspect the shape
cf zones list | jq '.[].id' # print each zone's id
cf zones list --name example.com # look up one domain
As the resource management docs show, cf zones list returns a JSON array in which each zone has an id field. Commands that operate inside a zone take --zone or its short form -z, and accept either a zone ID or a domain name:
cf dns records list --zone example.com
cf dns records list -z example.com
Find Commands with cf cli search
To find a command, run cf cli search with a plain-language description of the task, then open the help for the command it suggests. Per the agents docs, cf cli search returns up to five matches as a JSON array, best match first. It searches on your own machine, so you don’t need to be logged in.
cf cli search "create a DNS record"
cf dns --help
cf complete zsh >> ~/.zshrc # shell completion
--help works at every level of the command tree.
How Do You Configure Workers with cloudflare.config.ts?
cloudflare.config.ts is cf’s TypeScript configuration file for Workers. It imports defineConfig from cf/config, default-exports the result, and declares bindings and triggers through typed helpers that your editor can autocomplete. The programmatic configuration reference points out that the file is a normal TypeScript module, so you can use imports, functions, environment variables and async values in it.
The launch post’s first example imports the Worker module with the with { type: "cf-worker" } import attribute, then sets name, entrypoint and compatibilityDate. In the excerpts below, // ... stands in for those fields. This trimmed excerpt of the launch post’s bindings example passes a function to defineConfig so that values can vary by mode:
import { bindings, defineConfig } from "cf/config";
export default defineConfig(({ mode }) => ({
worker: {
// ...
env: {
API_TOKEN: bindings.secret(),
DATABASE: bindings.d1({ name: `example-${mode}-database` }),
UPLOADS: bindings.r2({ name: `example-${mode}-uploads` }),
JOBS: bindings.queue<{ userId: string }>({
name: `example-${mode}-jobs`,
}),
AI: bindings.ai(),
},
},
}));
The full example also uses bindings.text, bindings.kv, bindings.vectorize and bindings.worker. Triggers live in a single array. This excerpt passes a plain object to defineConfig:
import { defineConfig, triggers } from "cf/config";
export default defineConfig({
worker: {
// ...
triggers: [
triggers.fetch({ pattern: "example.com/*" }),
triggers.scheduled({ schedule: "0 * * * *" }),
triggers.queue({ name: "jobs", maxBatchSize: 10 }),
],
},
});
The launch post’s version adds a triggers.email entry. The reference also lists triggers.connect, which covers raw sockets. In the launch post, Cloudflare says it plans to let this file set up zones and DNS too. For now, the default export covers a Worker, Container applications and account settings.
Init, Deploy and Migrate
Use cf init for a new project, cf deploy to ship it, and cf migrate for an existing Wrangler project.
cf init my-worker # or `cf init .` inside an existing app
cf deploy
cf init creates cloudflare.config.ts and vite.config.ts, and in an existing Vite app it installs @cloudflare/vite-plugin. Run without arguments, it stops and asks for a directory. Per the deploy docs, one cf deploy call builds and checks the project, works out which credentials and account to use, then uploads a new Worker Version and makes it live. A static site needs no config file before its first cf deploy.
If your project has a wrangler.toml, wrangler.json or wrangler.jsonc, migrate it before you run cf dev, cf build or cf deploy. Those three commands only look at cloudflare.config.ts and ignore any Wrangler config file.
cf migrate --dry-run
cf migrate
cf migrate reads TOML directly, so you don’t need to convert old files first. If you only want a JSON view of a legacy wrangler.toml, use a TOML to JSON converter. The cf for Wrangler users guide explains that migration sticks with Wrangler’s bundler unless the project already uses the Cloudflare Vite plugin, so moving to Vite is optional. When it keeps Wrangler’s bundler, it writes a wrangler.config.ts, and the migration docs say the Worker’s package needs Wrangler 4.136.0 or later installed. The launch post adds that Rust and Python Workers also keep delegating to Wrangler.
What Happens to Wrangler?
Wrangler keeps working for now, but its end date is set. Once the cf open beta ends, Cloudflare plans to ship a final major Wrangler version that directs users to cf, then maintain Wrangler for 18 months from that point. Cloudflare hasn’t announced when the beta ends, so the 18 months has no calendar date yet.
Vite becoming the default build fits a wider move toward Vite in the Workers ecosystem.
Conclusion
You can install cf alongside Wrangler: its resource commands run in an unmigrated project, and the switch only matters when you move a Worker’s builds onto it. Install the official package, run cf zones list | jq . to see the output for yourself, then test cf migrate --dry-run on a branch of a non-critical Worker before the beta ends and Wrangler’s maintenance period starts.
FAQs
How do I use cf with more than one Cloudflare account?
Use named profiles. Run cf auth create work to create a profile and sign in to it, then cf auth activate work to link it to the current folder and every folder below it. Pass --profile work to use a profile for one command, and cf auth list to see all profiles. A CLOUDFLARE_API_TOKEN environment variable overrides every profile, and blocks profile management commands while set.
Can cf read credentials from a .env file?
Yes. API commands can pick up CLOUDFLARE_API_TOKEN, CLOUDFLARE_ACCOUNT_ID and CLOUDFLARE_ZONE_ID from a .env file in the folder you run them from. cf only looks at .env, not .env.local, so make sure .env is listed in .gitignore. A token loaded this way takes priority over any profile or stored login. cf does not support the Global API Key, so use a scoped API token instead.
How do I migrate a Wrangler project inside a monorepo?
Pass the config file path to cf migrate, for example cf migrate packages/api/wrangler.jsonc --dry-run, then run the same command without --dry-run. Without a path, cf migrate expects exactly one wrangler.json, wrangler.jsonc or wrangler.toml in the current directory. It writes its files next to the config file, picks the bundler from package.json unless you pass --bundler vite or --bundler wrangler, and needs --force if the Git worktree is not clean.
What happens if I run cf deploy in a Wrangler project before migrating?
cf ignores wrangler.jsonc and wrangler.toml, so it can set the project up on its own and change files such as package.json. To recover, run git status, restore each changed file with git restore, and delete new files such as cloudflare.config.ts, wrangler.config.ts and the .cloudflare directory. Then run cf migrate, which does not run while a cloudflare.config.ts exists.
Gain Debugging Superpowers
Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers.
Star on GitHub12k