How to deploy Vite apps to Cloudflare with Void
Explore with AI
Install Void as a dev dependency with npm install -D void, run npx void init and choose Cloudflare as the target, then run npx void deploy. Void builds the app with Vite, creates the D1, KV, R2, queue or AI resources your code imports, applies database migrations and prints the deployed URL. You need Node.js 24.21.0 or later, and you sign in to Cloudflare in the browser the first time you deploy.
On this page
- What is Void?
- How Void deploys differ from a hand-written Wrangler setup
- Deploying your first Vite app with Void, step by step
- Adding Void to an existing Vite app or Worker
- Deploying a Void app from GitHub Actions
- Running a shared Void Platform for your team
- What goes wrong on a first Void deploy
- Watching a Void app after the deploy lands
- Fixing Void app issues with Polylane
- Common questions
Void is new. Cloudflare announced it on 15 October 2026 in its post “Deploy with Void”. It is an open-source SDK, CLI and self-hosted platform from VoidZero, the team behind Vite, Vitest, Rolldown and Oxc. It turns a Vite app into a full-stack Cloudflare Workers app. You write server routes next to your UI, import a database or AI client, and void deploy provisions the matching Cloudflare resources and ships the app.
The short path is three commands: npm install -D void, npx void init, then npx void deploy. The rest of this page covers what Void changes compared with deploying a Worker by hand, the full setup, CI, the shared team platform, and the limits Cloudflare’s docs state.
What is Void?
Void is a full-stack framework and deploy tool for Cloudflare Workers, powered by Vite. In plain words: you keep building a Vite app, add one plugin, and Void gives you server routes, typed data access and a one-command deploy to Workers. It ships as one npm package and Vite plugin with three parts:
- Void SDK: file-based API routes, server-rendered pages, typed data access and adapters for UI frameworks. The docs list React, Vue, Svelte and Solid for Void pages. It also works alongside TanStack Start, React Router, SvelteKit, Nuxt, Analog and Astro, which keep their own routing and rendering while Void handles resources, migrations and deployment.
- Void CLI: builds the app, provisions the Cloudflare resources it uses, applies migrations and deploys. The same CLI reads logs, manages secrets and rolls back. It is built on Cloudflare’s new cf CLI.
- Void Platform: a shared deployment environment you install in your own Cloudflare account on top of Workers for Platforms, with access control, resource limits and usage reporting.
The project is MIT licensed and lives at voidzero-dev/void on GitHub. Cloudflare says VoidZero has run the SDK, CLI and Platform in production for several months.
Who it suits: teams building full-stack Vite apps on Workers who want resources to follow the code, and companies that want staff (including people who work mainly through coding agents) to ship internal apps without handing each of them a Cloudflare account. The announcement frames the Platform around exactly that second case.
How Void deploys differ from a hand-written Wrangler setup
With a plain Worker, you declare each binding in Wrangler config and run migrations as a separate step. Wrangler can now create the KV, D1 and R2 resources for the bindings you declare and link them to your Worker, but you still write each binding into the config. Void reads your source. It scans folders such as routes/, pages/, queues/, crons/ and src/, and each import maps to a binding:
void/dbgives you aDBbinding on D1void/kvgives youKVvoid/storagegives youSTORAGEon R2void/aigives youAIon Workers AIvoid/queuesgives youQUEUE_*bindings- a
defineDurableState()module indurable-objects/gives you a Durable Object named after the file
In development, Void provides local D1, KV and R2 data. On deploy, it creates the real resources and records their IDs in void.lock.json. Worker settings such as the compatibility date, flags, routes and service bindings live in void.config.ts. The docs say to commit both files and keep the generated Cloudflare config out of Git.
The deploy itself is staged. This is the order the Cloudflare integration guide gives:
graph TB A[Run void deploy] --> B[Create missing resources] B --> C[Build the app with Vite] C --> D{Secrets and migrations valid?} D -->|no| E[Deploy stops] D -->|yes| F[Save D1 restore point, migrate] F --> G[Upload the Worker] G --> H{Readiness check passes?} H -->|yes| I[Update traffic and triggers] H -->|no| J[Restore previous deployment] style E fill:#fee2e2,stroke:#fca5a5,color:#7f1d1d style J fill:#fee2e2,stroke:#fca5a5,color:#7f1d1d style I fill:#d1fae5,stroke:#6ee7b7,color:#065f46
Two details in that flow matter in production. Void verifies the uploaded version before it sends it traffic, and if readiness or trigger updates fail, it puts the previous deployment back. And resources are created first, so a build or validation failure can leave new resources in your account.
Deploying your first Vite app with Void, step by step
-
Install Void and run setup. Use Node.js 24.21.0 or later. In an empty directory, run:
npm install -D void npx void initSetup asks for a UI framework, a starter and a deployment target. Pick Cloudflare to deploy to your own account, or skip and choose later. Vite+ is the default build tool, and setup installs it without changing your package-manager versions or lockfiles. The docs suggest D1 as a first database because it runs locally with no separate server. pnpm, yarn and bun work too (
pnpm void init,yarn void init,bunx void init).void initalso adds a shortAGENTS.mdin new projects and links Void’s bundled skills for coding agents it detects. -
Check the Vite config. Setup adds the plugin to
vite.config.ts. For a React app it looks like this:import { defineConfig } from "vite"; import { voidPlugin } from "void"; import { voidReact } from "@void/react/plugin"; export default defineConfig({ plugins: [voidPlugin(), voidReact()], }); -
Run it locally. Start the dev server and open
http://localhost:5173:npm run dev -
Add a server route. Create
routes/api/hello.ts. The file path is the URL, so this serves/api/hello:import { defineHandler } from 'void'; export const GET = defineHandler(() => { return { message: 'Hello from Void' }; });Call it from the client with typed fetch, and TypeScript checks the caller against the handler’s return shape:
import { fetch } from 'void/client'; const { message } = await fetch('/api/hello');Database starters include
db/schema.ts, a Drizzle schema that defines your tables and query types. -
Declare environment variables. Create
env.tsat the project root and put local values in one root.envfile:import { defineEnv, string, url } from 'void/env'; export default defineEnv({ STRIPE_KEY: string(), VITE_API_ORIGIN: url(), }); -
Set production secrets. Deploy refuses to run until every required server key exists remotely, and it never uploads
.env:npx void secret put STRIPE_KEY npx void env check --remote -
Generate and commit migrations. If the schema has changes with no migration, deploy stops. This covers Better Auth tables too:
npx void db generate git add db/migrations git commit -m "Add migration" -
Deploy. Keys starting with
VITE_are client values embedded in browser JavaScript, so pass them through the build shell:VITE_API_ORIGIN=https://api.example.com npx void deployIf you skipped the target during setup, add
--platform cloudflare. Void opens the browser to sign in and asks which account to use if you have several. If the browser doesn’t open, follow the printed link and confirm the code. When it finishes, it prints the deployment URL in a summary box, and later deploys reuse the saved target. On your own account, Void saves a D1 Time Travel restore point before applying migrations and prints thevoid db restorecommand for it. If it can’t save one, it stops before migrating. -
Check status, read logs, roll back.
npx void project status npx void project logs npx void project rollbackRollback restores the code and the triggers recorded with that version. It does not reverse database migrations, so check the older code still works against the current schema first.
The CLI is meant to be driven by agents as well. The announcement says you can ask an agent to debug failing requests, read deployment logs or run the whole deploy.
Adding Void to an existing Vite app or Worker
For an existing Vite project, install Void and run void init. It adds voidPlugin() to the config and creates the remaining files, and it shows you the edit when the config needs a manual change. For a meta framework, follow its integration guide on void.cloud.
To move an existing Worker onto Void:
- Convert
wrangler.tomltowrangler.jsoncorwrangler.jsonand keep it at the project root. - If the Worker has a newer inactive upload, activate or remove it in Cloudflare.
- Make sure readiness checks can run: the Worker needs a version metadata binding or access to Version URLs, or Void stops before activation.
- Run
npx void deploywith no destination selected and accept the offer to link the Worker. Void moves bindings, variables, secrets, routes and schedules intovoid.config.tsandvoid.lock.json. - Commit both files. Keep this first deploy to the existing app, and add new resources, auth or migrations afterwards.
For a Durable Object Worker that Cloudflare can’t stage, void deploy --platform cloudflare --atomic sends traffic to the new version before the readiness check. Durable Object class migrations can’t be rolled back across their migration boundary.
Deploying a Void app from GitHub Actions
-
Generate a workflow for your saved target and package manager:
npx void init --github -
Add
CLOUDFLARE_API_TOKENto repository secrets. It needs Workers Scripts Edit, read access to bound resources, and edit access to the products Void provisions. -
Add
CLOUDFLARE_ACCOUNT_IDif the token can reach several accounts,DATABASE_URLfor PostgreSQL and MySQL apps, andCF_ACCESS_CLIENT_IDwithCF_ACCESS_CLIENT_SECRETif Cloudflare Access protects the readiness URL. Creating Hyperdrive needs a token with Hyperdrive edit permission. -
If CI typechecks before deploying, run
void prepareafter installing dependencies so the.void/types exist. -
Run the very first deploy from one machine and commit
void.lock.jsonbefore CI deploys, so CI reuses the resource IDs.
Running a shared Void Platform for your team
The Platform is for teams that want many people to deploy apps without each of them holding Cloudflare access. Admins control access, set resource limits and inspect deployments and usage through the CLI or an admin dashboard. Team platforms also keep log history, where direct Cloudflare deploys give you a live tail.
To self-host one, Cloudflare lists these requirements:
- a Cloudflare account with Workers for Platforms and R2 enabled
- a PostgreSQL database
- an account with your chosen login provider
- a domain for your apps
Users then point the CLI at the platform’s API URL and deploy with the same command:
npx void connect platform.example.com
npx void deploy --platform void --project my-app
For CI against a platform, set VOID_TOKEN, VOID_API_URL and the project slug, and run void connect "$VOID_API_URL" --no-login in each fresh environment. When an app goes over its request or CPU allowance, the platform returns a structured HTTP 429 to API requests and an error page to browsers. You can replace that page with public/usage-limit.html or public/execution-limit.html, written as standalone HTML with inline CSS.
What goes wrong on a first Void deploy
- Deploy stops on the schema. You changed
db/schema.tswithout a migration. Fix: runvoid db generate, review and commit the SQL, deploy again. - Deploy stops on a missing secret.
.envis local only. Fix:void secret put NAMEfor each server key inenv.ts, orvoid secret syncwith a file of production values. - A client value is wrong in production. Builds ignore
VITE_*values from.envon purpose. Fix: pass them in the build shell or give them a schema default. - Unsupported dotenv files.
.env.local,.env.productionand Wrangler’s.dev.vars*are not read. Fix: one root.env. - Orphaned resources after a failed first deploy. Resources are created before the build. Fix: deploy the first time from one machine, then commit
void.lock.json. - A bad migration is live. Rollback leaves the database alone. Fix:
void project rollbackif the new code is live,void db restoreto the saved point, then fix or remove the migration. Restoring drops every write since the restore point, and restore points expire after 7 days on Workers Free and 30 days on Workers Paid. - A resource import isn’t detected. Inference scans
.ts,.tsx,.jsand similar files plus Astro frontmatter. Vue and Svelte component files are not scanned. Fix: move the import into a.server.tscompanion, or useinference.bindingsto enable it. - Deploy clashes with a Cloudflare gradual rollout. Fix: finish or cancel the rollout in Cloudflare before deploying through Void.
- You use named Cloudflare environments. They aren’t supported. Fix: one Void project config per target.
- Development burns your AI allowance. Workers AI runs remotely once you connect to Cloudflare. Fix: keep that in mind when testing AI routes locally.
- Deleting a project leaves resources.
void project deletedoes not remove Cloudflare resources. Fix: remove them in the Cloudflare dashboard.
Plan and maturity limits from the docs: native apps run on Workers Free within its quotas, while Sandboxes need Workers Paid and Containers access, and email to arbitrary recipients needs Workers Paid. The vinext adapters are experimental. Node.js, Bun and Deno targets build a standalone server, and D1, KV, R2 and Workers AI bindings are unavailable there.
Watching a Void app after the deploy lands
Void’s checks cover the moment of release: readiness before traffic, automatic restore if it fails. What happens over the next hours is a separate job. On a direct Cloudflare deploy, void project logs is a live tail, which shows what is happening now. To answer which deploy started an error spike yesterday, you need logs and deploy history stored somewhere you can query. I built and led the Workers observability team at Cloudflare, and lining up deploys with errors is the first question in nearly every Workers incident.
Fixing Void app issues with Polylane
Polylane detects production issues and fixes them by opening pull requests. It connects to Cloudflare, so when a Void app breaks after a migration or binding change it works out why, and the fix arrives ready for your team’s normal review.
Running on Cloudflare? See how Polylane monitors Cloudflare in production.
Common questions.
When was Void announced and who makes it?
Cloudflare announced Void on 15 October 2026 in its blog post "Deploy with Void". It is built by VoidZero, the team behind Vite, Vitest, Rolldown and Oxc, which joined Cloudflare. The voidzero-dev/void repository on GitHub lists an MIT license.
Do I need a Cloudflare account to deploy with Void?
To deploy directly you need your own Cloudflare account, and the first void deploy opens a browser to sign you in. If your company runs a Void Platform, you run void connect with the admin's API URL and deploy to the platform without your own account. Native apps work on Workers Free within its quotas.
What Node.js version does Void need?
The README says to use Node.js 24.21.0 or later. Setup installs Vite+ as the default toolchain for you, and you don't need to install it globally.
Which Cloudflare resources does Void create automatically?
Void infers bindings from imports: void/db creates a D1 database, void/kv a KV namespace, void/storage an R2 bucket, void/ai a Workers AI binding and void/queues queue bindings. Modules in durable-objects/ become Durable Objects. Resource IDs are saved in void.lock.json, which you commit.
Does void project rollback undo database migrations?
No. Rollback restores code and recorded triggers only. On your own Cloudflare account, deploy saves a D1 Time Travel restore point before migrating, and you restore it with void db restore. Restore points expire after 7 days on Workers Free and 30 days on Workers Paid.
Can I move an existing Worker to Void?
Yes. Keep the root wrangler.jsonc or wrangler.json and run void deploy, and Void offers to link the Worker, moving its bindings, variables, secrets, routes and schedules into void.config.ts and void.lock.json. Convert wrangler.toml to JSON first, and note that named Cloudflare environments are not supported.
What does a self-hosted Void Platform need?
A Cloudflare account with Workers for Platforms and R2 enabled, a PostgreSQL database, an account with your chosen login provider and a domain for your apps. Users then connect with void connect and deploy with void deploy --platform void.
Why did my VITE_ variable not reach production?
Void builds ignore client values from .env so a developer's local value can't leak into production. Pass VITE_ values in the build shell, for example VITE_API_ORIGIN=https://api.example.com void deploy, or give them a default in env.ts. Server values go in with void secret put.
Sources (11)
- Deploy with Void (Cloudflare blog)
- voidzero-dev/void on GitHub
- What is Void? (Void docs)
- Quickstart (Void docs)
- Deployment (Void docs)
- Environment variables (Void docs)
- Cloudflare integration (Void docs)
- Resource Inference (Void docs)
- Automatic resource provisioning in Wrangler (Cloudflare changelog)
- Polylane full content
- Polylane docs
Boris Tane is the founder of Polylane. He previously founded Baselime, observability for the future of the cloud, which Cloudflare acquired. At Cloudflare he built and led the Workers observability team.
Related
- How to correlate incidents with deploys using evidence Learn how to correlate incidents with deploys using release markers, metrics, logs and verified GitHub, Vercel, Cloudflare and Kubernetes change events.
- What is change intelligence? A production safety guide What is change intelligence? Learn how deploys, config, flags, infrastructure and dependency changes connect to incidents, using tools you already have.
- How to Debug Cloudflare Workers Errors: Logs, Traces and Error Codes Debug Cloudflare Workers errors step by step: read 1101 and 1102 codes, enable Workers Logs and source maps, use wrangler tail, DevTools and local traces.
- Cloudflare Workers error 1101: causes and how to fix it Error 1101 means your Cloudflare Worker threw an uncaught JavaScript exception. Find the exception in logs, match it to its cause, fix it and roll back fast.
- How to monitor a vibe-coded app in production Monitor a vibe-coded app in production: surface swallowed errors, add a health route, tag deploys, watch cron jobs and alert on what users feel first.