DEV Community

jamilxt
jamilxt

Posted on

Deno Deploy Shuts Down in Six Months. Here Is the Migration Path to Cloudflare Workers.

On October 9, Ryan Dahl announced that the entire Deno team is joining Cloudflare. The post sat at the top of Hacker News within hours and crossed 1,200 points, which tells you how many developers had some part of their career or their side projects riding on this runtime.

The part that should make you open your calendar is not the acquisition itself. It is the timeline buried in the announcement:

  • Deno Deploy shuts down in six months. Paying customers get migration support to Cloudflare Workers, but the service goes dark around April 2027.
  • Deno runtime development ends in one year. Monthly releases with bug fixes and security updates continue for another year, then stop. Deno stays open source, so a community fork is possible, but the company behind it is moving on.
  • JSR survives. The JavaScript registry continues operating, with its infrastructure moving to Cloudflare.
  • rusty_v8 survives too, and Cloudflare plans to integrate it into workerd, the runtime that powers Workers.

If you deployed anything to Deno Deploy, your deadline is roughly April 2027. That sounds far away until you remember you have production traffic, a day job, and three other side projects. This article is the migration plan, written the way I wish migration guides were written: what maps to what, what does not map cleanly, and the decision checklist for whether you should migrate at all.

Full disclosure: I run cron-driven agents on my own server, and I verified every API detail here against Deno's and Cloudflare's official documentation, linked inline. I have not personally operated a Deno Deploy app in production, so this is a docs-verified porting map, not a war story.

Why Cloudflare bought Deno

The announcement is worth reading in full because it explains where server-side JavaScript is heading. Dahl's argument, which he first laid out in his JavaScript Containers post, is that compute, storage, and communication should come as one package instead of every application assembling its own infrastructure.

The concrete outcome is a project called celld, which Dahl describes as building on the Cloudflare Workers programming model so that scaling is part of the programming model itself rather than something you bolt on with autoscalers. The Deno team will combine forces with the Workers and Durable Objects teams.

The sentence that matters most for the next few years is about AI. Dahl writes that Durable Objects bring together capabilities that are particularly useful for agent harnesses: inexpensive serverless execution, persistent state, WebSockets, and a high-level JavaScript interface. In other words, the thing Cloudflare actually wanted from Deno is the talent and the ideas behind making server-side JS simple, and the destination is Durable Objects, not a standalone Deno runtime competing with Node.

The migration map

Every Deno Deploy app I have seen in the wild uses the same three primitives: a server started with Deno.serve, some state in Deno KV, and maybe a cron. Here is where each one lands on Cloudflare.

  • Deno.serve() becomes a Worker fetch handler. Workers are event-driven, so there is no server to start. Your route handling logic ports almost line for line.
  • Deno KV becomes Workers KV for simple reads, or Durable Object storage when you need transactions. This is the one real design decision in the migration, covered below.
  • Deno.cron() becomes a Cron Trigger configured in your Wrangler file with a scheduled handler.
  • npm dependencies mostly just work. Workers enable Node.js compatibility (nodejs_compat) by default for compatibility dates of 2026-08-04 or later, per Cloudflare's compatibility flags docs, so code that imports Node built-ins runs without extra flags.
  • Static files move into the assets config. Wrangler serves a static directory and falls through to your Worker for API routes.

Step 1: The skeleton

A Deno Deploy app starts like this:

Deno.serve((req) => {
  const url = new URL(req.url);
  if (url.pathname === "/api/hello") {
    return Response.json({ message: "hello" });
  }
  return new Response("not found", { status: 404 });
});
Enter fullscreen mode Exit fullscreen mode

The Workers equivalent keeps your handler logic and changes only the wrapper. Create a project with npm create cloudflare@latest, then:

export default {
  async fetch(request, env, ctx) {
    const url = new URL(request.url);
    if (url.pathname === "/api/hello") {
      return Response.json({ message: "hello" });
    }
    return new Response("not found", { status: 404 });
  },
};
Enter fullscreen mode Exit fullscreen mode

That is the whole server migration. The differences that bite people are subtle: there is no long-lived process, so no in-memory globals that survive between requests, and module-level state can be evicted at any time. Anything you cached in a top-level variable on Deno Deploy needs real storage on Workers.

Step 2: Deno KV, the decision that actually matters

Deno KV is the nicest key-value API in mainstream JavaScript, and it is going away with the runtime. From the Deno KV docs, the signature look is array keys, stored JavaScript objects, and a versionstamp on every entry:

const kv = await Deno.openKv();
await kv.set(["preferences", "ada"], { theme: "dark" });
const entry = await kv.get(["preferences", "ada"]);
// entry.value.theme, entry.versionstamp
Enter fullscreen mode Exit fullscreen mode

Cloudflare gives you two destinations, and picking between them is the core of your migration plan:

  • Workers KV (getting started guide) is globally replicated and built for read-heavy workloads: config data, routing tables, feature flags, auth tokens. Keys are strings, not arrays, so ["preferences", "ada"] becomes "preferences:ada". Values are strings, so your objects get JSON-encoded. There is no atomic transaction across keys and no compare-and-set.
  • Durable Object storage (storage API docs) gives each object its own private, transactional, strongly consistent storage. Cloudflare now recommends the SQLite-backed flavor for all new namespaces, which adds real SQL tables and point-in-time recovery for the past 30 days. The KV-style methods (get, put, list) are still there, but writes can be grouped transactionally, which is what Deno KV's atomic() was giving you.

The same docs show how a DO class is declared and provisioned in wrangler.jsonc:

{
  "exports": {
    "Counter": { "type": "durable-object", "storage": "sqlite" }
  }
}
Enter fullscreen mode Exit fullscreen mode

The honest version of the rule:

  • Read-mostly data that one key at a time covers? Workers KV. It is the simpler port and the cheaper operation at read-heavy scale.
  • Counters, rate limiters, session state, anything with check-then-set logic? A Durable Object. Deno KV's atomic operations with versionstamp checks have a real equivalent here, and pretending Workers KV has transactions is how you ship a race condition.

Step 3: Crons

On Deno Deploy you wrote Deno.cron("refresh", "0 * * * *", handler). On Workers, crons are configuration plus a scheduled handler:

{
  "triggers": { "crons": ["0 * * * *"] }
}
Enter fullscreen mode Exit fullscreen mode
export default {
  async scheduled(event, env, ctx) {
    // your old cron body
  },
  async fetch(request, env, ctx) {
    // ...
  },
};
Enter fullscreen mode Exit fullscreen mode

Same cron syntax, different doorway.

Step 4: Frontend assets

If your Deploy project served a built frontend alongside the API, Wrangler handles it with the assets block instead of a separate static host:

{
  "assets": { "directory": "./dist/" },
  "main": "./src/worker.ts"
}
Enter fullscreen mode Exit fullscreen mode

Per the routing docs, Cloudflare tries a static asset match first and falls through to your Worker script when nothing matches, which reproduces the single-origin setup most Deno Deploy apps had.

Should you migrate to Workers at all?

Cloudflare would love for the answer to be yes, and for greenfield agent-shaped projects it is a reasonable yes. But you have three options and one of them is "do almost nothing yet."

  • Stay on Deno Deploy until the deadline, then reassess. Deploy runs for six more months and the runtime gets security patches for twelve. If your app is a low-traffic side project, waiting costs you nothing and buys time to see whether a community fork materializes. Deno remains open source; the announcement explicitly welcomes others to continue it.
  • Migrate to Workers. The right call when you use Deploy's global KV, want zero-ops hosting for free-tier traffic, or want the Durable Objects primitives for stateful features. The port above is a weekend, not a rewrite.
  • Move to plain Node, Bun, or a self-hosted box. The right call when your app is boring HTTP plus a database. Deno.serve ports to any runtime's server in an afternoon, your Postgres stops caring where it lives, and you owe no vendor a migration ever again. If you already run a server, this is the least new-thing option.

What I would actually do, in order: export your Deno KV data now and stash it somewhere boring, pick the Workers path if and only if you use Deploy-specific features, and put a recurring reminder two months before the shutdown. The data export is the part people leave until it is awkward.

The bigger lesson

Two runtime vendors tried to reinvent server-side JavaScript in the last decade and both ended up converging on the same idea: edge-deployed isolates with state attached. Node won the ecosystem war, but the interesting programming model work is happening in the Workers lineage, and now Deno's team is inside that lineage too.

If Dahl is right that Durable Objects are the natural home for AI agent harnesses, the migration path in this article is also the on-ramp to the next thing. The people who port cleanly are the ones who map their primitives early instead of the week before the shutdown.

I write about backend engineering, developer tools, and AI every week. Subscribe, it is free.

Have you shipped anything on Deno Deploy? Are you porting to Workers, forking, or walking away? Tell me in the comments.

Top comments (1)

Collapse
 
dev_in_the_fog profile image
Jason Y. (dev_in_the_fog) •

Great write-up. The structured step-by-step reasoning made following along very engaging. Keep up the awesome work!