Remote development
Normally the SDK uses local D1, KV and R2 through Miniflare. Remote mode runs your local code against a project deployed to your SDK platform, for example to test with real data or investigate an issue.
On this pageTurn on remote mode · Know what runs locally and what runs remotely · Stay safe with production data
Calls such as db.select() and env.KV.get() use the deployed resources. Your code does not change. Writes affect real data, so use a staging project when you can.
Prerequisites
Complete these two steps before you turn on remote mode.
Requires: platform login
void connect <url> signs you in to your team's platform when needed. Run it on its own and finish the sign-in first.
- Connect to your team's platform with
void connect <url>. - Link a project with
void project link. Deploy it at least once so its resources exist.
Turn on remote mode
Persistent: void.config.ts
This setting turns on remote mode for every dev session.
{
"remote": true
}One-off: environment variable
This step starts one dev session in remote mode.
VOID_REMOTE=1 vite devExample output: the dev server starts and prints the remote bindings banner shown below.
VOID_REMOTE=0 turns remote mode off, even when void.config.ts sets "remote": true.
Supported bindings
| Binding | Local (default) | Remote |
|---|---|---|
| D1 | Local SQLite via Miniflare | Remote D1 database |
| KV | Local file-backed KV | Remote KV namespace |
| R2 | Local file-backed R2 | Remote R2 bucket |
| AI | Always proxied | Always proxied |
AI requests always use your platform’s account and allowance. There is no local AI simulator.
Check remote mode
With remote mode on, the dev server prints this banner at startup:
[void] Remote bindings active
DB → remote D1
KV → remote KV
STORAGE → remote R2Limitations
- Network latency: each binding call is a network request. Responses can be slower than local development.
- R2 multipart uploads:
createMultipartUpload()andresumeMultipartUpload()are not supported in remote mode. - R2 range and conditional reads: in remote mode,
get()ignores request headers passed asrangeoronlyIfand returns the whole object. For example,get(key, { range: request.headers, onlyIf: request.headers })returns the whole object when the request has aRangeorIf-None-Matchheader.get()throws whenrangeoronlyIfis an object that asks for a byte range or a condition, such as{ offset: 0, length: 100 }or{ etagMatches: etag }. - R2 conditional writes:
put(..., { onlyIf })requires an updated platform and project deployment. Failed preconditions returnnull. - R2 metadata and list options: custom metadata, HTTP metadata, and
list()withdelimiter,startAfter, orincluderequire an updated project deployment. Without one,list()with these options andput()with metadata other thancontentTypethrow an error. - D1 dump:
db.dump()is not supported in remote mode. - D1 batches:
db.batch()requires an updated platform and project deployment.