Set maxRetransmits on Cloudflare Realtime SFU DataChannels

Set maxRetransmits on Cloudflare Realtime SFU DataChannels

Cloudflare Realtime SFU DataChannels expose the same delivery knobs as WebRTC DataChannel creation: ordered, maxRetransmits, and maxPacketLifeTime. ordered controls message order on its own. The retry fields set how much effort the browser makes to deliver each message.

Do not treat ordered delivery and retry budgets as the same thing. A channel can be unordered and still allow retries, or ordered and still use a limited retry budget. The useful boundary is simple: order decides how messages arrive relative to each other, retries decide how hard the browser tries before giving up.

Match delivery policy on both sides of the negotiated channel

Negotiated DataChannel IDs are fixed at setup, so the remote browser does not discover the delivery policy from the peer. The Cloudflare Realtime SFU API request and the browser createDataChannel() call both need the same settings, or the channel behaves differently at each end.

Use the same values in the publisher request, subscriber request, and browser call. If one side asks for ordered delivery and the other side asks for unordered delivery, the channel is still negotiated, but the behaviour is no longer something you can rely on without checking the exact setup.

Keep ordered delivery and retry budgets separate

Ordered delivery blocks later messages until earlier ones arrive. That is fine for a stream of commands that must be processed in sequence. It is awkward for live state updates, where a stale packet can sit in the way of newer ones and add latency for no gain.

Unordered delivery avoids that blockage. Newer messages can pass delayed ones, which is usually the point for state that changes quickly. Retry budgets are a separate decision. A channel can be unordered and still retry a small number of times, or it can give up very quickly if the payload is already stale.

Mirror the same settings in the publisher request, subscriber request, and browser call

The API endpoints for channel creation sit under the session IDs for the publisher and subscriber, and both use the same bearer token. The browser side then creates a negotiated channel with the returned ID.

A minimal match for an unordered, no-retry state channel looks like this:

js
// Publisher request
await fetch(https://rtc.live.cloudflare.com/v1/apps/${APP_ID}/sessions/${PUBLISHER_SESSION_ID}/datachannels/new, {
method: ‘POST’,
headers: {
‘Authorization’: Bearer ${APP_TOKEN},
‘Content-Type’: ‘application/json’
},
body: JSON.stringify({
location: ‘local’,
dataChannelName: ‘player-state’,
ordered: false,
maxRetransmits: 0
})
});

js
// Subscriber request
await fetch(https://rtc.live.cloudflare.com/v1/apps/${APP_ID}/sessions/${SUBSCRIBER_SESSION_ID}/datachannels/new, {
method: ‘POST’,
headers: {
‘Authorization’: Bearer ${APP_TOKEN},
‘Content-Type’: ‘application/json’
},
body: JSON.stringify({
location: ‘remote’,
sessionId: PUBLISHERSESSIONID,
dataChannelName: ‘player-state’,
ordered: false,
maxRetransmits: 0
})
});

js
// Browser
const channel = pc.createDataChannel(‘player-state’, {
negotiated: true,
id: channelId,
ordered: false,
maxRetransmits: 0
});

The exact names matter. So do the values. A browser call with different retry settings from the server-side request is a nice way to create a debugging session for later.

Pick the retry model that fits the payload

Live state updates are not all equal. Some can arrive late and still be useful. Others are dead on arrival the moment a newer value exists. The channel settings should follow that difference instead of pretending every payload deserves the same treatment.

Use maxRetransmits for state where a stale update is useless

maxRetransmits limits how many times the browser will try again before dropping a message. Set it when the payload still matters for a short window, but loses value once newer state has replaced it.

For live state updates, that usually means small deltas, control events, and transient game state. If the packet misses its slot, there is no sense dragging it into the present after the state has moved on.

A common low-latency pattern is:

js
pc.createDataChannel(‘player-state’, {
negotiated: true,
id: channelId,
ordered: false,
maxRetransmits: 0
});

That gives unordered delivery with no retries. It is blunt, but for fast-changing state it often matches the payload better than pretending reliability is free.

Use maxPacketLifeTime when time matters more than retry count

maxPacketLifeTime sets a time limit rather than a retry count. Use it when the message can survive a small amount of delay, but not a long chase through the network.

This fits cases where the payload is still relevant for a short period, but not long enough to justify repeated retransmission attempts. It is a timed budget, not a guarantee. Once the lifetime expires, the browser drops the message even if it has not been delivered.

Only one retry budget should be set on a channel. maxRetransmits and maxPacketLifeTime are alternatives, not a pair to stack together. For a given channel, pick the one that matches how the payload ages.

Apply the channel settings in code and verify the result

The basic rule is short: set ordered: false for unordered delivery, then choose either maxRetransmits or maxPacketLifeTime. If the channel needs plain reliable ordered delivery, omit both retry fields and leave ordered unset.

A practical check is to verify the negotiated channel ID on both sides and confirm the browser call uses the same delivery policy as the API requests. If live state updates still arrive in the wrong order or stall behind old packets, the channel was configured for the wrong failure mode.

Related posts

Removed SSRF beta detections in Cloudflare WAF

Cloudflare WAF Managed Rules have a habit of changing underneath you, and old SSRF names can linger long after the protection has gone. I prefer to check the live block logs, then test the ugly...

Weekly Tech Digest | 28 Sep 2026

Stay updated with the latest in tech! This digest covers AI ethics, auto industry shifts, and the impact of politics on technology, exploring today's pressing issues.

HomeAssistant Core | 2026.9.4

HomeAssistant Core 2026 9 4: integration, logging fixes, Tuya fans off at speed 0, deps bumped Enphase and Xbox, HTTP scan cost accuracy improved