- Published on
Deno Is Joining Cloudflare: What Existing Apps Need to Move
- Authors

- Name
- Mehdi Akiki
Reference · Through the layers
Your app is working. Then the platform announces a different future, and suddenly you are reading migration documents instead of finishing the feature you planned for the week.
For Deno users, this is a real question now. In its October 9, 2026 announcement, the team said it was joining Cloudflare. It announced another year of runtime fixes and security updates, followed by the end of the team's runtime development. Deno Deploy would continue operating for six months before shutting down. Those are different timelines for different things.
I would start there. An application using the Deno runtime on a server you control has a different problem from an application depending on Deno Deploy services. Writing down which one you have is more useful than immediately choosing a replacement framework.
Keeping the runtime in a container doesn't extend that support period. The runtime remains open source, but a future maintenance plan needs an actual owner; it shouldn't depend on an assumed continuation by someone else.
Find what belongs to the host
Consider a small webhook service. It receives an event, saves it, and runs a cleanup task each night. The TypeScript handler may be easy to move. Its database, scheduled task, secrets, and file storage may need separate work.
Make an inventory with actual names. For this imaginary webhook service, I would use these four rows:
| Part | Find in the existing app | Check in the destination | Acceptance check |
|---|---|---|---|
| HTTP entry point | Deno.serve, request size and signature handling | Supported request API and body limits | The same signed event gets the same response |
| Database | Driver, service name, backup owner and export method | Connection support, transaction behavior and restore path | Restore a copy and read the expected event IDs |
| Scheduled cleanup | Trigger, timezone, retry rule and last success | A scheduler with the required timing and retry behavior | An overdue sample expires once, with a recorded completion |
| Uploaded files | Temporary directory or durable store, keys and retention | Required read/write API and lifetime | An uploaded sample survives only for its intended lifetime |
Don't put “database handled by platform” and leave it there. Find out how to export it, how to restore it, and which part of the application assumes it is nearby. A successful export that nobody can restore is not much of an exit plan.
Check background work too. An HTTP route can pass every request check while the nightly cleanup has quietly stopped. Give each scheduled task an owner and a way to see its last successful completion.
Move one runtime dependency out of the way
Environment access is a small place to start. Suppose several modules call Deno.env.get directly. You can put the rule for a required setting in one ordinary function:
export function requiredSetting(read: (name: string) => string | undefined, name: string): string {
const value = read(name);
if (!value) throw new Error(`Missing setting: ${name}`);
return value;
}
The Deno entry point passes name => Deno.env.get(name). A Node entry point can pass name => process.env[name]. Most application code no longer needs to know which runtime read the value.
This is also a useful place to separate configuration from access. Passing a reader into a function makes it easier to substitute; it doesn't restrict the Node process's environment. Preserve any required access limits separately. If your agent jobs depend on Deno Sandbox, the Deno Sandbox permission checklist covers that different deployment boundary.
This doesn't make a database driver portable or turn a server into a Worker. It gives you one smaller change to inspect. Keep the existing behavior for missing and empty settings, and keep secret values out of errors and logs.
Deno's permission model is another dependency to record. If your process relies on restricted filesystem or network access, moving it to a runtime with different defaults changes more than the startup command. Decide where those restrictions will be enforced after the move.
Choose a destination around the difficult parts
There are several possible paths. Keeping the current runtime in a container can reduce the immediate hosting change. Porting to Node may suit an application already built around Node libraries. Moving to Workers requires checking the APIs and services the application actually uses.
The right choice depends on your app. A service with local file assumptions needs different work from one using only HTTP requests and an external database.
If the move changes how dependencies are installed, inspect the npm lifecycle scripts and installation permissions too. A build step can have access that the application runtime does not.
Take the hardest dependency and make a small trial around it. Can the chosen environment use the database driver? Can it keep the required connection behavior? Can it process the largest allowed event within its limits? A tiny hello-world deployment doesn't answer those questions.
Write down what would make the trial fail. Perhaps the driver cannot run there, or the storage location creates unacceptable latency. Finding that early is a useful result. It is cheaper than discovering it after rewriting the routes.
Keep the old path usable during the move
Before switching traffic, save a few sanitized request examples and their expected responses. Include a repeated webhook, a rejected signature, and a temporary storage failure. Run the same checks against the old and new implementations through CI.
Keep the webhook's existing authentication and retry rules. Don't combine the hosting move with a new event format or a different duplicate policy unless the migration requires it. Those extra changes make differences harder to explain.
If possible, compare read-only requests first. For writes, decide which deployment owns the operation. Sending the same event to both versions can create duplicates or repeat a payment-side action. Shadow traffic needs its own rules.
Rollback also needs a data plan. The old image may start perfectly and still fail if the new version changed the database schema in a way it cannot read. Keep schema changes compatible with both versions during the switch, or document a separate recovery procedure.
Before switching off the old deployment, check every item in the inventory. Can requests reach the app? Can it read the old data? Did last night's cleanup finish? Keep the old path available until those checks pass, with a clear decision about which deployment accepts writes during the switch.