Skip to main content
Some data changes many times per second — cursor positions, player movement, animation state, drag coordinates. Sending every update through reliable, ordered delivery is overkill. Volatile paths give you a fast lane for this kind of data.

How volatile paths work

Volatile paths are special paths in your database optimized for high-frequency updates. When you write to a volatile path:
  1. The write resolves immediately on the client. There’s no waiting for a server acknowledgment.
  2. The server batches and coalesces volatile updates before broadcasting them. If a client writes 60 cursor positions per second, subscribers don’t receive all 60. They get the latest value at each broadcast interval.
  3. Only the most recent value for each path is sent. Intermediate states are dropped.
The result is low-latency, low-bandwidth data delivery — perfect for real-time interactions where you only care about the current state.
// Writing to a volatile path — resolves instantly
db.ref('cursors/alice').set({
  x: 450,
  y: 312
});

Volatile vs. regular writes

Regular writesVolatile writes
AcknowledgmentServer confirms the writeResolves immediately, no server ack
DenialsServer sends an error the client can handleSilently dropped
DeliveryReliable, orderedBest-effort, latest-value-wins
PersistenceWritten to durable storageNot persisted — in-memory only
BroadcastEvery change is sent to subscribersBatched and coalesced before broadcast
TransportTCP (WebSocket) or QUIC streams (WebTransport)TCP (WebSocket) or UDP datagrams (WebTransport)
Over WebTransport, volatile writes use UDP datagrams — truly unreliable delivery for maximum speed. Over WebSocket, they’re sent as regular TCP messages but are still batched and coalesced on the server before broadcast.

Batching rates

The server broadcasts volatile updates at fixed intervals:
  • WebTransport clients: ~20 Hz (every 50ms). At most 20 volatile updates per second reach each subscriber.
  • WebSocket clients: ~7 Hz (every 150ms). Slower than WebTransport due to TCP overhead, but still fast enough for smooth updates.
We recommend tuning the frequency at which you write to volatile paths to match the transport the client is connected on. For example, on WebSocket you might write 10 times per second, while on WebTransport you could write 20 times per second.

Size limit

Each volatile write is limited to 2 KB. Keep your volatile payloads small — coordinates, velocity vectors, flags. If you need to send more, break it into multiple paths.
The 2 KB limit is enforced per write. If your volatile payload exceeds it, the write will be dropped. Stick to small, focused data — positions, not full game state.

Configuring volatile paths

Mark a path as volatile by adding ".volatile": true in your security rules:
{
  "rules": {
    "cursors": {
      "$userId": {
        ".volatile": true,
        ".read": true,
        ".write": "auth.uid === $userId"
      }
    }
  }
}
Any path under a .volatile rule automatically uses volatile delivery. You don’t need to change your write code — the same set() and update() calls just behave differently under the hood.

Rules restrictions on volatile paths

Volatile paths support .read, .write, and .validate rules, but they must be simple rules — rules that don’t reference existing database state. Specifically, a rule on a volatile path cannot contain data. or root. anywhere in the expression. These require reading current state from storage, which is incompatible with the volatile fast path. Any rule variable that doesn’t require reading existing state — auth, newData, $wildcards, now, literals, and operators — works fine. The two that don’t work are data and root, since both require reading current database state.
If a volatile path has a rule that references data or root, the rule evaluates to false — the read or write is silently denied. There’s no error message; the operation just fails. Make sure your volatile path rules only use the allowed variables listed above.

Volatile snapshots

When you receive a volatile update through a subscription, the snapshot includes extra metadata:
db.ref('cursors/bob').on('value', (snapshot) => {
  if (snapshot.isVolatile()) {
    const serverTime = snapshot.getServerTimestamp();
    const position = snapshot.val();

    // Use server timestamp for interpolation
    interpolateCursor(position, serverTime);
  }
});
  • snapshot.isVolatile() — Returns true if this update arrived via volatile delivery.
  • snapshot.getServerTimestamp() — The server timestamp when the volatile value was broadcast. Use this for interpolation or lag compensation in games.

When to use volatile paths

Volatile paths are the right choice when:
  • The data changes many times per second
  • You only care about the latest value, not every intermediate state
  • Missing an update is acceptable (the next one will arrive shortly)
Good candidates:
  • Cursor and pointer positions
  • Player position and velocity in games
  • Typing indicators
  • Drag-and-drop coordinates
  • Animation state and transforms
  • Camera position in collaborative 3D editors
Bad candidates:
  • Scores and leaderboards (every increment matters)
  • Inventory and currency (can’t afford to miss a change)
  • Chat messages (users expect to see every message)
  • Game actions and events (order and completeness matter)
Volatile data is not persisted to disk — it lives in memory only. If the server restarts, volatile data is gone. This is by design — volatile paths are for ephemeral, high-frequency data where the next update is always right behind the current one.

What’s next