cloudflare · · 9 min read

Cloudflare cf CLI: Getting started and migrating from Wrangler

Install Cloudflare cf CLI, preview DNS, D1, and cache requests, and decide when to migrate a Wrangler project. Includes configuration examples, agent permissions, and a build-once CI workflow.

Mttao Mttao @bearboy80 1,955 words 中文 →
Cloudflare cf CLI: Getting started and migrating from Wrangler

Deploy a Worker with Wrangler, open the dashboard to change DNS, then look up an API endpoint to write a cache purge script. If you use Cloudflare, that routine probably sounds familiar. The new cf CLI brings those operations into one command-line tool, alongside local development and deployment for Workers.

I’d start with resource queries and request previews. For a new Worker, try it in a separate project. For a Wrangler project that’s already running reliably, inspect the migration before changing deployment commands. cf is still in public beta, and its configuration format and Build Output can change. There’s no need to move every project at once.

Cloudflare's official cf CLI launch illustration

Figure 1: Cloudflare’s cf CLI launch illustration, from the official announcement.

Where to start using cf

cf generates API commands from schemas and returns JSON by default. Workers projects use cloudflare.config.ts for configuration. You can use the CLI for either resource management or project development, but the starting point depends on what you’re working on:

What you’re doingWhere to startWhat to check
Creating a Worker projectcf init → cf dev → cf buildLocal routes, bindings, and build modes
Managing DNS, cache, R2, D1, or other resourcesQueries, cf schema, and --dry-runAccount, zone, permissions, and request scope
Maintaining a Wrangler projectcf migrate --dry-runBundler choice, follow-up items, and the existing build process
Giving a coding agent access to CloudflareSearch → inspect the schema → previewToken scope and permission to execute the change

Your Worker can keep deploying through Wrangler while you use cf to manage DNS or query resources. Deal with project migration when you’re ready to switch to cf dev or cf deploy.

Install, sign in, and pin a version

Check your Node.js version first: cf requires 22.18 or later. Bun isn’t supported as a runtime; commands that load cloudflare.config.ts fail on it. The package installs both cf and cloudflare, which run the same CLI. If another tool already uses the name cf, use cloudflare.

npm install --global cf
cf --version

# Command discovery doesn't require a login
cf cli search "create a DNS record"
cf schema dns records create

Searching for commands and reading schemas don’t need credentials. Sign in when you want to read remote resources or make a change:

cf auth login
cf auth whoami
cf zones list

You need to sign in to cf even if you’re already signed in to Wrangler. Credential lookup starts with CLOUDFLARE_API_TOKEN, followed by the profile selected with --profile, the profile bound to the current directory or its nearest parent, and finally the default login profile. Global API Keys aren’t supported.

A global install is convenient for trying the tool. Once you use it in a project, pin the version. Otherwise, your local commands might work while CI downloads a different beta release. After migrating an existing project, add the version you’ve checked to its development dependencies and commit the lockfile:

# Replace the placeholder with a version you've verified
npm install --save-dev --save-exact 'cf@<VERSION>'
npx cf --version

Projects created with cf init already include the cf dependency. See the getting started documentation for installation and authentication details.

Search when you don’t know the command

More product coverage means more commands to remember. With cf cli search, you can describe the task and get candidate commands: create a DNS record, create a D1 database, or purge a URL from cache.

How API schemas, project configuration, and users feed into cf CLI, with separate request preview and execution steps

Figure 2: Command generation and execution in cf. A dry run stops at the request preview. Making the change requires a separate invocation without --dry-run.

cf cli search "create a DNS record"
cf cli search "create D1 database"
cf cli search "purge cached files for a URL"

Once you find a candidate, inspect its arguments:

cf schema dns records create
cf dns records create --help

cf schema shows the API request structure for a generated command. --help shows how to invoke it. If you’re writing a script, pay attention to output streams too: results normally go to standard output as JSON, while status messages and errors go to standard error. Commands that download files return raw data, so don’t send every result through a JSON parser.

Preview resource changes with a dry run

Take DNS. Knowing that you want to add a record isn’t enough. You also need the right zone, destination IP, and proxy setting.

Add --dry-run to a generated API command to see its HTTP method, URL, parameters, and body. It sends no API request and needs no credentials. You can inspect the request before running it, but a preview can’t confirm that the remote resource exists or that your token has permission to access it.

DNS: Add an A record

cf cli search "create a DNS record"
cf schema dns records create

# Replace the placeholder with a zone ID; this won't create a record
cf dns records create \
  --zone '<ZONE_ID>' \
  --body '{"type":"A","name":"docs","content":"192.0.2.1","proxied":true}' \
  --dry-run

192.0.2.1 is a documentation address. Replace it with your origin’s IP before executing the command. Check the zone and record name in the preview as well. proxied: true enables Cloudflare’s proxy; choose that setting with your origin access and TLS configuration in mind.

There’s an easy-to-miss difference here: use a zone ID for the dry run. Normal execution accepts a domain name with --zone, but a dry run doesn’t look up its ID. Pass a domain name and the preview puts it directly into the ID position in the URL.

After reviewing the request and configuring credentials with the required permissions, remove --dry-run to execute it. Then query the record:

cf dns records list --zone example.com --name docs.example.com

The resource management documentation explains these options.

D1: Create the database, then configure the binding

Suppose you need an orders-staging database for staging. Preview the request first:

cf cli search "create D1 database"
cf schema d1 create
cf d1 create --name orders-staging --dry-run

Confirm the database name and target account, set CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID, then create it:

cf d1 create --name orders-staging

Next comes the Worker binding in cloudflare.config.ts. Creating the database successfully doesn’t guarantee that the Worker points to it. When troubleshooting, check the database first, then the binding. The configuration example below selects a database name by environment.

Cache: Purge the file you changed

If you’ve updated one JavaScript file, purge that URL. A full purge would also make other cached files fetch from the origin again:

cf cli search "purge cached files for a URL"
cf schema cache purge

cf cache purge \
  --zone '<ZONE_ID>' \
  --body '{"files":["https://example.com/assets/app.js"]}' \
  --dry-run

The preview shows POST /zones/<ZONE_ID>/purge_cache, with just that file URL in the body. Check the complete URL and file list for the wrong environment or a missing path.

These are examples; they haven’t been executed against a live account. After upgrading the beta, check cf schema and --help again for parameter changes.

Configure Workers with TypeScript

Start a new project in its own directory:

cf init edge-api
cd edge-api
cf dev

Get the routes and bindings working locally before building and deploying. New projects use Vite by default, with configuration in cloudflare.config.ts.

That file can return different settings based on mode. Builders such as bindings and triggers provide type hints, so you don’t have to remember every field. For example, you can select a D1 binding by environment:

import { bindings, defineConfig } from "cf/config";

export default defineConfig(({ mode }) => ({
  worker: {
    name: "orders-api",
    compatibilityDate: "2026-09-27",
    env: {
      DB: bindings.d1({ name: `orders-${mode}` }),
    },
  },
}));

With --mode staging, this selects orders-staging; with --mode production, it selects orders-production. The snippet only shows the binding. You still need to create the database, as in the earlier example. A full project also needs an entry point and build configuration; see the programmatic configuration documentation.

Turning repeated environment settings into a function is convenient. But TypeScript configuration executes code and can read environment variables or import modules. Follow those dependencies during review and check which Worker and database each mode selects.

For local debugging, Local Explorer lets you inspect supported KV, R2, D1, and other resources. Only some commands support --local. Unsupported commands return an error rather than switching to remote resources; check the documentation for the command you’re using.

Inspect migration before switching a Wrangler project

If a repository has wrangler.json, wrangler.jsonc, or wrangler.toml but no cloudflare.config.ts, hold off on cf dev, cf build, and cf deploy. Those commands can trigger automatic configuration that ignores the existing Wrangler settings, or they can fail. Start with cf migrate.

Create a feature branch, commit or stash your current changes, then preview the migration:

git status
cf migrate --dry-run

The preview lists the files it would change and the follow-up work. It doesn’t show the full contents of generated files. To inspect the actual configuration, run the migration and review the diff:

cf migrate
git status
git diff

The original Wrangler configuration stays in place. The tool creates cloudflare.config.ts alongside it, adds cf to the project’s dependencies, and updates the lockfile.

Check the bundler choice separately. If the project declares @cloudflare/vite-plugin, the migration selects Vite. Otherwise, it selects Wrangler and creates wrangler.config.ts. cf migrate doesn’t install Vite or generate vite.config.ts, so switching bundlers requires extra work.

Settings that need manual review are marked with TODO(@cloudflare). When required items remain, the generated configuration includes a throw that stops the build. Resolve those items before removing it. Durable Object bindings and migration history need particular care; deleting the comments doesn’t resolve the underlying work.

Migration and preview commands can return exit status 1 when required items remain. Read the follow-up list before treating the exit code as a conversion failure. The migration guide covers bundler requirements and follow-up items.

Once the configuration is ready, run the project locally and check staging:

cf dev
# Check local behavior, stop the development server, then continue
cf build --mode staging
cf deploy --dry-run --mode staging

Check every environment the project uses. A successful staging build says nothing about whether production resource names and routes are correct. Run the existing build steps too, before changing the production deployment workflow.

Agents: Check credentials and exit codes

An agent can search for commands, read schemas, and present a request preview for review. Put the execution sequence in the project’s instructions:

  1. Find candidate commands with cf cli search.
  2. Read cf schema and the command’s --help.
  3. Generate a preview with --dry-run.
  4. Check the account, zone, request body, and permitted scope.
  5. Execute with restricted credentials, then query the resulting state.

Review flow for Cloudflare changes: find a command, inspect its schema, preview the request, review its scope, execute, and verify

Figure 3: Checks for a Cloudflare change. After inspecting the preview, decide whether to allow execution. Query the actual state afterward.

Use separate profiles for personal and work accounts. Unattended agents and CI jobs should use API tokens with only the permissions they need. Even if the project instructions require a review, the credentials should restrict which resources the agent can change.

One behavior is easy to miss in scripts: a destructive command without --force in a non-interactive session can print Aborted., make no change, and still exit with status 0. The pipeline looks successful, but the resource is still there. Check standard error and query the resource afterward.

Don’t add --force to every command just to keep a script running. For some commands it’s also an API parameter and can change the scope of the operation. Read that command’s help first; the coding agents documentation explains the behavior.

Build once in CI

Build and preview in the pull request stage without a production token. At deployment time, use --prebuilt to upload the output you’ve already checked:

# PRs and other branches: build and preview without uploading or calling the Cloudflare API
npx cf build --mode production
npx cf deploy --prebuilt --mode production --dry-run

# Protected deployment step: provide credentials and deploy the same output
npx cf deploy --prebuilt --mode production

--prebuilt skips the build and uses existing Build Output. Both steps here use --mode production; a mode mismatch stops deployment before anything is uploaded. If build and deployment run in separate CI jobs, pass .cloudflare/output between them so the deployment job has the checked output.

Provide CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID only to the final deployment step. Building and previewing don’t call the Cloudflare API, though dependency installation and custom build scripts can still access the network. The CI documentation has a complete example.

For an existing project, I’d make the migration criteria concrete: resolve every migration TODO, pass builds and previews for each environment, and verify an actual deployment in a test environment before changing production CI. If the Wrangler setup is working reliably, keep that deployment process while you try cf for resource queries and API request previews.

References

Mttao

Mttao GitHub ↗

Exploring technology and life's wisdom

Related Posts

View all →
  1. 01 The Complete Cloudflare Wrangler Guide: From Local Development to Global Deployment Cloudflare· Aug 15, 2025
  2. 02 Cloudflare Workers Custom Domains vs Routes cloudflare· Aug 22, 2026
  3. 03 How to Fix Cloudflare Error 521: Don't Clear the Cache, Trace the Path cloudflare· Sep 19, 2026

/ Comments