Deploy
Put the worker on your Cloudflare account. Use the Deploy button, or deploy from your computer with Wrangler.
1. Make the keys
In a copy of this repo, run:
npm run keysWithout the code, Node.js 22 or newer can make them too:
node -e 'for (const r of ["DEVICE", "VIEWER"]) { const k = require("crypto").generateKeyPairSync("ed25519"); console.log(`${r}_PRIVATE_KEY=${k.privateKey.export({ format: "der", type: "pkcs8" }).toString("base64url")}`); console.log(`${r}_PUBLIC_KEY=${k.publicKey.export({ format: "der", type: "spki" }).subarray(-32).toString("base64url")}`) }'Keep all four values. The private keys go into the worker. The public keys go to your devices and viewers.
2. Create a Realtime SFU app
- In the Cloudflare dashboard, go to Realtime > SFU.
- Create an application for production.
- Copy its App ID and API token.
3. Deploy
With the Deploy button
- Click the button.
- Cloudflare copies this repo to your GitHub or GitLab account.
- Enter the four secrets:
REALTIME_SFU_APP_ID,REALTIME_SFU_BEARER_TOKEN,DEVICE_PRIVATE_KEYandVIEWER_PRIVATE_KEY. - Leave Protect with Cloudflare Access off, or choose Previews only. All traffic blocks every device and viewer. To protect the dashboard, see Dashboard.
- Click Deploy.
Each push to your copy deploys it again.
With Wrangler
git clone https://github.com/mazupo/cf-sfu-worker.git
cd cf-sfu-worker
npm install
npx wrangler login
npm run deployThen set the secrets. Each command asks for the value:
npx wrangler secret put REALTIME_SFU_APP_ID
npx wrangler secret put REALTIME_SFU_BEARER_TOKEN
npx wrangler secret put DEVICE_PRIVATE_KEY
npx wrangler secret put VIEWER_PRIVATE_KEYTo update later, run git pull and npm run deploy.
4. Check it
The worker runs at https://cf-sfu-worker.<your-subdomain>.workers.dev.
curl https://cf-sfu-worker.<your-subdomain>.workers.dev/sfu/healthIt answers {"service":"cf-sfu-worker","ok":true}. Then open /sfu/device and /sfu/ on the same host, as in Run it on your computer.
Use your own domain
The pages and the API are all under /sfu/. Add a route in wrangler.jsonc, and deploy again:
"routes": [
{ "pattern": "example.com/sfu", "zone_name": "example.com" },
{ "pattern": "example.com/sfu/*", "zone_name": "example.com" }
],
"workers_dev": falseA route does not create a DNS record. example.com must already have one that Cloudflare proxies, for example a website or another Worker's custom domain. For a new host, use a custom domain instead: { "pattern": "sfu.example.com", "custom_domain": true }.
The rest of example.com is not affected, but nothing else on it can use paths under /sfu/. The mazupo environment in wrangler.jsonc is the mazupo deployment. You do not need it.
When you deploy again
A deploy closes every socket. Devices and viewers connect again within seconds, and the streams keep going.
Next
- Configuration: limits, TURN and the allowed origins.
- Dashboard: protect
/sfu/admin/.

