{
  "$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"
}