cheat sheet
wrangler
Cloudflare's official CLI for building, configuring, and deploying Workers, Pages, KV, R2, D1, Vectorize, Hyperdrive, Queues, Workflows, and Secrets.
wrangler — Cloudflare Workers CLI
What it is
Wrangler is Cloudflare's official command-line tool for developing and deploying Cloudflare Workers and the Workers Platform (Pages, KV, R2, D1, Vectorize, Hyperdrive, Queues, Workflows, Containers, Secrets Store, Workers AI). It is written in TypeScript, distributed via npm, and pairs with a project-level wrangler.jsonc (or legacy wrangler.toml) that declares the Worker's entry point, compatibility date, bindings, and environments. Reach for it any time you want to run a Worker locally with wrangler dev, deploy with wrangler deploy, or provision a Cloudflare resource without clicking through the dashboard. The main alternative is to drive the Cloudflare REST API directly — Wrangler is almost always the better choice because it understands bindings, generates TypeScript types, and simulates the runtime locally.
Install
Wrangler is published as the wrangler npm package. Install it as a project dev-dependency (preferred — pins per-project) or globally.
# Per-project (preferred — keeps the version pinned in package.json)
npm install -D wrangler@latest
# Global install
npm install -g wrangler
# One-off invocation without installing
npx wrangler --version
Output: (none — exits 0 on success)
Verify the install and check that you are on v4 or newer:
wrangler --version
Output:
⛅️ wrangler 4.20.1
Syntax
The base shape is a top-level command (often a resource: kv, r2, d1, pages), an optional subcommand, then flags. Most commands read wrangler.jsonc from the current directory unless --config is passed.
wrangler <command> [subcommand] [args] [--flag value] [--env <name>] [--config <path>]
Output: (none — exits 0 on success)
Essential options
| Option | Meaning |
|---|---|
--config <path> | Use a non-default config file path. |
--env <name> | Target a named environment (e.g. staging, production) from env.* in config. |
--cwd <dir> | Run the command as if invoked from <dir>. |
--dry-run | Validate and bundle without uploading (deploy commands only). |
--remote | Use real Cloudflare resources instead of local simulation (dev, d1 execute). |
--local | Force local simulation (default for wrangler dev). |
--minify | Minify the deployed Worker bundle. |
--keep-vars | Preserve variables set in the dashboard rather than overwriting from config. |
--name <name> | Override the Worker name from config. |
--help | Show help for the current command or subcommand. |
Authentication
Wrangler authenticates either by an OAuth browser login (developer machines) or a CLOUDFLARE_API_TOKEN environment variable (CI). The token must have Workers Edit and the scopes for any resource you touch (KV, R2, D1, etc.).
# Interactive OAuth login (opens your browser)
wrangler login
# Show the currently-authenticated account and token scopes
wrangler whoami
# Log out
wrangler logout
Output (wrangler whoami):
👋 You are logged in with an OAuth Token, associated with the email alice@example.com.
┌────────────────────┬──────────────────────────────────┐
│ Account Name │ Account ID │
├────────────────────┼──────────────────────────────────┤
│ Alice Dev's Account│ 1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d │
└────────────────────┴──────────────────────────────────┘
🔓 Token Permissions: workers:write, kv:write, r2:write, d1:write
For CI, set the token instead of running login:
export CLOUDFLARE_API_TOKEN="ey...your-token..."
export CLOUDFLARE_ACCOUNT_ID="1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d"
wrangler deploy
Output: (none — exits 0 on success)
Configuration (wrangler.jsonc)
wrangler.jsonc is the single source of truth for a Worker's entry point, compatibility settings, and bindings. JSONC (JSON with comments) is the modern format; new binding types ship JSON-first and may never reach the legacy wrangler.toml. Always add the $schema line — your editor will then autocomplete every field.
{
"$schema": "./node_modules/wrangler/config-schema.json",
"name": "alice-api",
"main": "src/index.ts",
"compatibility_date": "2026-05-01",
"compatibility_flags": ["nodejs_compat"],
"vars": {
"ENVIRONMENT": "production",
"OWNER_EMAIL": "alice@example.com"
},
"kv_namespaces": [
{ "binding": "CACHE", "id": "abcdef0123456789abcdef0123456789" }
],
"r2_buckets": [
{ "binding": "ASSETS", "bucket_name": "alice-assets" }
],
"d1_databases": [
{
"binding": "DB",
"database_name": "alice-app",
"database_id": "00000000-0000-0000-0000-000000000000",
"migrations_dir": "./migrations"
}
],
"ai": { "binding": "AI" },
"env": {
"staging": {
"name": "alice-api-staging",
"vars": { "ENVIRONMENT": "staging" }
}
}
}
Output: (none — exits 0 on success)
After every config change, regenerate the typed Env interface so your TypeScript code sees the new bindings:
wrangler types
Output:
Generating project types...
✓ Generated runtime types
✓ Generated env types at worker-configuration.d.ts
Local development
wrangler dev runs the Worker on a local Workerd runtime — the same one Cloudflare uses in production — with bindings simulated locally by default. State persists under .wrangler/state/ between runs.
# Default — local runtime, local bindings, port 8787
wrangler dev
# Pick a port
wrangler dev --port 3000
# Run the staging environment
wrangler dev --env staging
# Surface scheduled / cron handlers at http://localhost:8787/__scheduled
wrangler dev --test-scheduled
Output (wrangler dev):
⛅️ wrangler 4.20.1
─────────────────────────────────────────────
[wrangler:info] Ready on http://localhost:8787
Press 'b' to open a browser, 'd' to open Devtools, 'x' to exit
To talk to real Cloudflare resources from your laptop (handy when the data is too big to mirror locally, or for AI which is always remote), add "remote": true to that binding:
{
"r2_buckets": [
{ "binding": "ASSETS", "bucket_name": "alice-assets", "remote": true }
],
"ai": { "binding": "AI", "remote": true }
}
Output: (none — exits 0 on success)
Put local-only secrets in .dev.vars (git-ignore this file):
API_KEY=local-dev-key-1234
DATABASE_URL=postgres://alice@localhost:5432/dev
Deploying Workers
wrangler deploy bundles the Worker, uploads it to Cloudflare, and points the configured route (or <name>.<subdomain>.workers.dev) at the new version. The first deploy creates the Worker.
# Deploy to production
wrangler deploy
# Validate and bundle without uploading
wrangler deploy --dry-run --outdir dist
# Deploy a named environment
wrangler deploy --env staging
# Minify the bundle
wrangler deploy --minify
Output:
Total Upload: 42.13 KiB / gzip: 13.07 KiB
Worker Startup Time: 11 ms
Your Worker has access to the following bindings:
- KV Namespaces:
- CACHE: abcdef0123456789abcdef0123456789
- R2 Buckets:
- ASSETS: alice-assets
Uploaded alice-api (1.45 sec)
Published alice-api (4.12 sec)
https://alice-api.alicedev.workers.dev
Current Deployment ID: 9f8e7d6c-5b4a-3210-fedc-ba9876543210
Profile Worker startup before shipping — Workers must initialize in under 400 ms:
wrangler check startup
Output:
Worker startup time: 18 ms
Startup limit: 400 ms — OK
CPU profile written to .wrangler/startup-profile.cpuprofile
Pages deployments
Cloudflare Pages hosts static sites and SPAs. Wrangler can deploy a built dist/ directory directly — handy for Astro, SvelteKit, Vite, and similar projects.
# First-time project creation (interactive)
wrangler pages project create alice-site
# Deploy a built directory
wrangler pages deploy ./dist --project-name alice-site
# Deploy as a preview (any branch other than the production branch)
wrangler pages deploy ./dist --project-name alice-site --branch preview-feature
# List recent deployments
wrangler pages deployment list --project-name alice-site
Output (wrangler pages deploy):
✨ Compiled Worker successfully
✨ Success! Uploaded 84 files (1.23 sec)
✨ Deployment complete! Take a peek over at https://abc12345.alice-site.pages.dev
KV namespaces
KV is an eventually-consistent key-value store ideal for caching small read-heavy values. Wrangler can create namespaces, list keys, and bulk-import data.
# Create a namespace and grab its ID
wrangler kv namespace create CACHE
# List namespaces
wrangler kv namespace list
# Write a key (TTL in seconds)
wrangler kv key put --binding CACHE "user:42" '{"name":"Alice Dev"}' --expiration-ttl 3600
# Read it back
wrangler kv key get --binding CACHE "user:42"
# Bulk import from JSON: [{"key":"k1","value":"v1"},{"key":"k2","value":"v2"}]
wrangler kv bulk put --binding CACHE seed.json
# Delete
wrangler kv key delete --binding CACHE "user:42"
Output (wrangler kv namespace create CACHE):
🌀 Creating namespace with title "alice-api-CACHE"
✨ Success!
Add the following to your configuration file in your kv_namespaces array:
[[kv_namespaces]]
binding = "CACHE"
id = "abcdef0123456789abcdef0123456789"
R2 buckets
R2 is S3-compatible object storage with no egress fees. Wrangler creates buckets, uploads and downloads objects, and can configure lifecycle rules.
# Create a bucket (optionally hint the region: wnam, enam, weur, eeur, apac)
wrangler r2 bucket create alice-assets --location wnam
# Upload a file
wrangler r2 object put alice-assets/avatars/alice.png --file ./local/alice.png
# Download to a path (writes to stdout if --file is omitted)
wrangler r2 object get alice-assets/avatars/alice.png --file ./out.png
# Delete an object
wrangler r2 object delete alice-assets/avatars/alice.png
# Delete a bucket (must be empty)
wrangler r2 bucket delete alice-assets
Output (wrangler r2 bucket create):
Creating bucket 'alice-assets' (Standard) in jurisdiction 'default' with location hint 'wnam'...
✅ Created bucket 'alice-assets' with default storage class set to Standard.
D1 databases
D1 is a serverless SQLite-on-the-edge database. Wrangler creates databases, runs ad-hoc SQL, and manages numbered migrations.
# Create a database (grab the database_id from the output)
wrangler d1 create alice-app
# Run a query against the remote database
wrangler d1 execute alice-app --remote --command "SELECT count(*) FROM users"
# Run a SQL file locally (against the local replica under .wrangler/state)
wrangler d1 execute alice-app --local --file ./schema.sql
# Create a numbered migration
wrangler d1 migrations create alice-app add_email_to_users
# Apply pending migrations to the remote database
wrangler d1 migrations apply alice-app --remote
# Export schema + data for backup
wrangler d1 export alice-app --remote --output backup.sql
Output (wrangler d1 execute --command "SELECT count(*) FROM users"):
🌀 Executing on remote database alice-app (00000000-0000-0000-0000-000000000000)
🌀 To execute on your local development database, pass the --local flag.
┌──────────┐
│ count(*) │
├──────────┤
│ 1247 │
└──────────┘
Secrets and environments
Secrets are encrypted environment variables — never put them in wrangler.jsonc (committed) or .dev.vars for production. wrangler secret put prompts for the value interactively so it never appears on the command line or in your shell history.
# Interactive prompt (preferred — value never hits the shell)
wrangler secret put API_KEY
# From a file (for PEM keys, JSON, or CI pipelines)
wrangler secret put PRIVATE_KEY < ./keys/private.pem
# Bulk import — secrets.json must be {"K1":"v1","K2":"v2"}
wrangler secret bulk secrets.json
# List secret names (values never printed)
wrangler secret list
# Delete a secret
wrangler secret delete API_KEY
# Target an environment
wrangler secret put API_KEY --env staging
Output (wrangler secret list):
[
{ "name": "API_KEY", "type": "secret_text" },
{ "name": "PRIVATE_KEY", "type": "secret_text" }
]
Tail and observability
wrangler tail streams live request logs (and console.log output) from a deployed Worker. Combined with observability.enabled in config, you also get persistent logs visible in the dashboard.
# Stream all events
wrangler tail
# Filter to errors only
wrangler tail --status error
# Substring filter
wrangler tail --search "user_id=42"
# JSON output (pipe-friendly)
wrangler tail --format json
Output (wrangler tail):
Connected to alice-api, waiting for logs...
GET https://alice-api.alicedev.workers.dev/api/users - Ok @ 5/24/2026, 10:14:02 AM
(log) cache hit for user:42
GET https://alice-api.alicedev.workers.dev/api/posts - Exception Thrown @ 5/24/2026, 10:14:05 AM
(error) TypeError: Cannot read properties of undefined (reading 'id')
Enable persistent logs in config:
{
"observability": {
"enabled": true,
"head_sampling_rate": 1
}
}
Output: (none — exits 0 on success)
Versions and rollback
Every deploy produces an immutable version. Wrangler can list them and roll back instantly if a deploy goes bad.
# List the last few versions
wrangler versions list
# Show one in detail
wrangler versions view 9f8e7d6c-5b4a-3210-fedc-ba9876543210
# Roll back to the previous version
wrangler rollback
# Roll back to a specific version
wrangler rollback 9f8e7d6c-5b4a-3210-fedc-ba9876543210 --message "revert: broken users endpoint"
Output (wrangler rollback):
? Are you sure you want to deploy this Worker Version to 100% of traffic? › yes
Performing rollback...
✓ Deployed alice-api version 8e7d6c5b-4a32-10fe-dcba-9876543210ff at 100%
Common pitfalls
- Forgetting
wrangler typesafter editing bindings — yourEnvinterface goes stale and the new binding isany. Re-run after every config change. - Missing
compatibility_date— Workers default to a very old runtime. Set it to a date within the last 30 days; bump it quarterly. nodejs_compatnot enabled — Node built-ins likenode:cryptoornode:bufferthrow at runtime. Add"compatibility_flags": ["nodejs_compat"].- Committing
.dev.vars— these are local-only secrets. Add the file to.gitignoreimmediately. - Using
wrangler.tomlfor new bindings — Hyperdrive, Workflows, and other recent bindings are JSONC-first; migrate towrangler.jsonc. d1 executewithout--remoteor--local— Wrangler will prompt, but in CI it will hang. Always pass one explicitly.- Passing secret values on the command line — they get logged in
~/.bash_historyand CI logs. Usewrangler secret put(interactive) or<redirection from a file. - Hitting the 400 ms startup limit — heavy top-level imports break cold starts. Profile with
wrangler check startupand lazy-import heavy modules inside handlers.
Real-world recipes
Scaffold a new TypeScript Worker
Quickest path from empty directory to a deployed Hello-World Worker.
npm create cloudflare@latest alice-api -- --type=hello-world --ts --git
cd alice-api
npm install
wrangler dev # iterate locally at http://localhost:8787
wrangler deploy # ship to alice-api.alicedev.workers.dev
Output (final line of wrangler deploy):
Published alice-api (3.21 sec)
https://alice-api.alicedev.workers.dev
Current Deployment ID: 1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d
Promote a tested build from staging to production
Two named environments, deployed independently; the same code reaches production only after staging is verified.
# Push to staging first
wrangler deploy --env staging
curl -s https://alice-api-staging.alicedev.workers.dev/health
# Tail staging while you smoke-test
wrangler tail --env staging --status error
# When green, deploy to production
wrangler deploy --env production
Output (curl ... /health):
{"status":"ok","env":"staging","version":"9f8e7d6c"}
Seed a D1 database from a SQL file in CI
Idempotent migrations applied on every CI build, then a single ad-hoc verification query.
export CLOUDFLARE_API_TOKEN="$CLOUDFLARE_API_TOKEN"
export CLOUDFLARE_ACCOUNT_ID="$CLOUDFLARE_ACCOUNT_ID"
wrangler d1 migrations apply alice-app --remote
wrangler d1 execute alice-app --remote --command "SELECT count(*) AS users FROM users"
Output:
🌀 No migrations to apply.
┌───────┐
│ users │
├───────┤
│ 1247 │
└───────┘
Build and deploy an Astro site to Pages
Static-site flow used by this very project — build locally, push dist/ straight to Pages without a GitHub integration.
npm run build # astro build + pagefind
wrangler pages deploy ./dist --project-name alice-site
Output:
✨ Compiled Worker successfully
✨ Success! Uploaded 142 files (2.04 sec)
✨ Deployment complete! Take a peek over at https://7c3b1a92.alice-site.pages.dev
Rapid rollback after a bad deploy
Pulled the wrong branch, deployed, and the error rate jumped — get back to the previous version in one command.
wrangler tail --status error & # watch errors in another pane
wrangler versions list # find the last known-good version ID
wrangler rollback 8e7d6c5b-4a32-10fe-dcba-9876543210ff \
--message "revert: 500s from broken users query"
Output (wrangler rollback):
✓ Deployed alice-api version 8e7d6c5b-4a32-10fe-dcba-9876543210ff at 100%
Rollback complete in 2.8 sec