{
"$type": "site.standard.document",
"bskyPostRef": {
"cid": "bafyreibs4hf3kohicrbz2adb6vjyy5j4rdiupa5ofrboei3js5smz4zpqu",
"uri": "at://did:plc:25rdn5elo5izoxrmtis34zuk/app.bsky.feed.post/3mpkg534suel2"
},
"coverImage": {
"$type": "blob",
"ref": {
"$link": "bafkreiesyiwv2m7xtlwmmvttae642ogwewjr4jwxb72esqjxv6owyw7yo4"
},
"mimeType": "image/webp",
"size": 68796
},
"path": "/riversea/wrangler-dev-remote-silently-writes-to-your-production-kv-namespace-heres-the-fix-2c6p",
"publishedAt": "2026-07-01T01:08:50.000Z",
"site": "https://dev.to",
"tags": [
"cloudflare",
"serverless",
"webdev",
"tutorial",
"Full post →"
],
"textContent": "I lost production data on a Tuesday afternoon because `wrangler.toml` had one missing field. Not a code bug. Not a logic error. A missing `preview_id`.\n\nBy default, `wrangler dev` uses a local SQLite simulation — safe, isolated, zero real traffic. The moment you add `--remote`, every KV read and write goes to the actual Cloudflare namespace over the API. If your `wrangler.toml` only has the `id` field pointing at your production namespace, those writes land in prod. No warning. No confirmation prompt. Just silent data mutation on the namespace your live users depend on.\n\nThe fix is a single extra field:\n\n\n\n [[kv_namespaces]]\n binding = \"MY_STORE\"\n id = \"PROD_NAMESPACE_ID_HERE\"\n preview_id = \"DEV_NAMESPACE_ID_HERE\"\n\n\nWrangler automatically routes `--remote` traffic through `preview_id` instead of `id`. Create a separate dev namespace with `wrangler kv namespace create \"MY_STORE_dev\"`, drop its ID into `preview_id`, and your production namespace is untouched. This should probably be in the quickstart docs. It isn't, at least not prominently.\n\nThe second thing worth knowing: `--remote` exposes a behavioral gap that local simulation hides entirely. Local KV is synchronous and in-process — a `put()` followed by a `get()` on the same key always returns the fresh value. Remote KV is eventually consistent. I had a rate-limiting worker that looked completely broken under `--remote`: I'd write a counter, immediately read it back, and get the old value. The worker was correct. The local simulation had been lying to me about how production actually behaves. Switching to `--remote` (against a dev namespace, not prod) surfaced the real race condition. That's uncomfortable, but it's accurate.\n\nThere's also a write-rate ceiling worth knowing before you run any kind of seed script: hit roughly 1,000 writes/minute and you'll start seeing `429 Too Many Requests` with error code `10013`. A 70ms sleep between writes keeps you under the limit without dramatically slowing a seed operation down.\n\nI wrote up the full breakdown — including the `wrangler tail` JSON truncation trap that cost me two hours, a shell script for seeding a dev namespace with representative data, and the exact `cacheTtl: 0` pattern for honest read behavior — over on dailymanuallab.com.\n\nFull post →",
"title": "`wrangler dev --remote` silently writes to your production KV namespace — here's the fix"
}