# How to deploy Vite apps to Cloudflare with Void

> Void, announced by Cloudflare on 15 October 2026, deploys Vite apps to Workers in one command. Setup steps, secrets, migrations, CI and the gotchas.

By Boris Tane, Founder of Polylane · Published October 17, 2026 · Updated October 18, 2026 · 12 min read
Canonical: https://polylane.com/learn/deployment-safety/how-to-deploy-vite-apps-to-cloudflare-with-void/

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.

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/db` gives you a `DB` binding on D1
- `void/kv` gives you `KV`
- `void/storage` gives you `STORAGE` on R2
- `void/ai` gives you `AI` on Workers AI
- `void/queues` gives you `QUEUE_*` bindings
- a `defineDurableState()` module in `durable-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:

```mermaid alt="Steps void deploy runs on Cloudflare, where it stops early and when it restores the previous deployment"
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

1. **Install Void and run setup.** Use Node.js 24.21.0 or later. In an empty directory, run:

   ```bash
   npm install -D void
   npx void init
   ```

   Setup 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 init` also adds a short `AGENTS.md` in new projects and links Void's bundled skills for coding agents it detects.

2. **Check the Vite config.** Setup adds the plugin to `vite.config.ts`. For a React app it looks like this:

   ```ts
   import { defineConfig } from "vite";
   import { voidPlugin } from "void";
   import { voidReact } from "@void/react/plugin";

   export default defineConfig({
     plugins: [voidPlugin(), voidReact()],
   });
   ```

3. **Run it locally.** Start the dev server and open `http://localhost:5173`:

   ```bash
   npm run dev
   ```

4. **Add a server route.** Create `routes/api/hello.ts`. The file path is the URL, so this serves `/api/hello`:

   ```ts
   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:

   ```ts
   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.

5. **Declare environment variables.** Create `env.ts` at the project root and put local values in one root `.env` file:

   ```ts
   import { defineEnv, string, url } from 'void/env';

   export default defineEnv({
     STRIPE_KEY: string(),
     VITE_API_ORIGIN: url(),
   });
   ```

6. **Set production secrets.** Deploy refuses to run until every required server key exists remotely, and it never uploads `.env`:

   ```bash
   npx void secret put STRIPE_KEY
   npx void env check --remote
   ```

7. **Generate and commit migrations.** If the schema has changes with no migration, deploy stops. This covers Better Auth tables too:

   ```bash
   npx void db generate
   git add db/migrations
   git commit -m "Add migration"
   ```

8. **Deploy.** Keys starting with `VITE_` are client values embedded in browser JavaScript, so pass them through the build shell:

   ```bash
   VITE_API_ORIGIN=https://api.example.com npx void deploy
   ```

   If 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 the `void db restore` command for it. If it can't save one, it stops before migrating.

9. **Check status, read logs, roll back.**

   ```bash
   npx void project status
   npx void project logs
   npx void project rollback
   ```

   Rollback 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.

```text
> deploy this app with void, then read the project logs and tell me which requests fail
```

*Void's CLI and bundled skills let a coding agent run the deploy and read logs.*

## 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:

1. Convert `wrangler.toml` to `wrangler.jsonc` or `wrangler.json` and keep it at the project root.
2. If the Worker has a newer inactive upload, activate or remove it in Cloudflare.
3. Make sure readiness checks can run: the Worker needs a version metadata binding or access to Version URLs, or Void stops before activation.
4. Run `npx void deploy` with no destination selected and accept the offer to link the Worker. Void moves bindings, variables, secrets, routes and schedules into `void.config.ts` and `void.lock.json`.
5. 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

1. Generate a workflow for your saved target and package manager:

   ```bash
   npx void init --github
   ```

2. Add `CLOUDFLARE_API_TOKEN` to repository secrets. It needs Workers Scripts Edit, read access to bound resources, and edit access to the products Void provisions.
3. Add `CLOUDFLARE_ACCOUNT_ID` if the token can reach several accounts, `DATABASE_URL` for PostgreSQL and MySQL apps, and `CF_ACCESS_CLIENT_ID` with `CF_ACCESS_CLIENT_SECRET` if Cloudflare Access protects the readiness URL. Creating Hyperdrive needs a token with Hyperdrive edit permission.
4. If CI typechecks before deploying, run `void prepare` after installing dependencies so the `.void/` types exist.
5. Run the very first deploy from one machine and commit `void.lock.json` before 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:

```bash
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.ts` without a migration. Fix: run `void db generate`, review and commit the SQL, deploy again.
- **Deploy stops on a missing secret.** `.env` is local only. Fix: `void secret put NAME` for each server key in `env.ts`, or `void secret sync` with a file of production values.
- **A client value is wrong in production.** Builds ignore `VITE_*` values from `.env` on purpose. Fix: pass them in the build shell or give them a schema default.
- **Unsupported dotenv files.** `.env.local`, `.env.production` and 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 rollback` if the new code is live, `void db restore` to 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`, `.js` and similar files plus Astro frontmatter. Vue and Svelte component files are not scanned. Fix: move the import into a `.server.ts` companion, or use `inference.bindings` to 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 delete` does 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](https://polylane.com/for/cloudflare/).

## 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

- [Deploy with Void (Cloudflare blog)](https://blog.cloudflare.com/deploy-with-void/)
- [voidzero-dev/void on GitHub](https://github.com/voidzero-dev/void)
- [What is Void? (Void docs)](https://void.cloud/guide/)
- [Quickstart (Void docs)](https://void.cloud/guide/quickstart)
- [Deployment (Void docs)](https://void.cloud/guide/deployment)
- [Environment variables (Void docs)](https://void.cloud/guide/env-vars)
- [Cloudflare integration (Void docs)](https://void.cloud/integrations/cloudflare)
- [Resource Inference (Void docs)](https://void.cloud/reference/resource-inference)
- [Automatic resource provisioning in Wrangler (Cloudflare changelog)](https://developers.cloudflare.com/changelog/post/2025-10-24-automatic-resource-provisioning/)
- [Polylane full content](https://polylane.com/llms-full.txt)
- [Polylane docs](https://docs.polylane.com/llms-full.txt)

## About the author

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](https://polylane.com/learn/deployment-safety/how-to-correlate-incidents-with-deploys/): 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](https://polylane.com/learn/deployment-safety/what-is-change-intelligence/): 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](https://polylane.com/learn/troubleshooting/how-to-debug-cloudflare-workers-errors/): 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](https://polylane.com/learn/troubleshooting/cloudflare-workers-error-1101/): 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](https://polylane.com/learn/monitoring-coverage/vibe-coding-production-monitoring/): 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.

Get started with one command: `curl -fsSL https://polylane.com/setup | bash` installs the CLI, connects your coding agents, and creates the account.
