Migrate from Cloudflare Workers
Workers is V8-isolate edge compute; ours is V8-isolate plus Python, Rust, Bun, Deno, and full WASI. The handler signature is the same — Request in, Response out — so the migration is mostly config, not code.
The handler diff
// Workers (export default form)
- export default {
- async fetch(request, env, ctx) {
- return new Response("hi");
- },
- };
// Joule Cloud Functions
+ export default async function (req) {
+ return new Response("hi");
+ }
Deploy
# Workers
- wrangler deploy
# Joule Cloud
+ invisible fn deploy worker.js \
+ --route /api \
+ --runtime node-22
Service map
| Cloudflare | Joule Cloud |
|---|---|
| Workers | Functions |
| R2 | Object Store |
| D1 / Hyperdrive | JouleDB |
| KV | JouleDB (small + indexed) or Object Store |
| Durable Objects | Stateful Functions w/ region-pinned db row — pattern, not primitive |
| Workers AI | Inference |
| Queues | Functions + queue trigger |
| Pages | Static via Object Store + edge cache, or Functions for SSR |
R2 → Object Store
R2 already exposes the S3 API surface, so client-side it's just swapping the endpoint. See Migrate from AWS S3 for SDK config (Boto3, AWS SDK v3, etc.).
Bindings → env vars
Workers wires resources (KV, R2, Queues) into env via bindings declared in wrangler.toml. Joule Cloud injects them as standard environment variables declared in invisible.hcl:
workload "api" {
image = "..."
env = {
DATABASE_URL = database.main.url
OBJECT_BUCKET = bucket.uploads.url
}
}
Cron triggers
- # wrangler.toml
- [triggers]
- crons = ["0 * * * *"]
+ # Joule Cloud
+ invisible fn deploy job.js --cron "0 * * * *"
What you keep
- V8 isolate cold start < 10 ms — same architecture
- Edge presence — the mesh has 101 nodes incl. low-latency points in EU, NA, APAC
- Free in-mesh data transfer (matches R2's zero-egress economics)
What you gain
- Python, Rust, Bun, Deno, WASI — not just V8
- Per-invocation joule receipt (energy used + carbon at that moment)
- One billing surface for Functions + Database + Object Store