Self-hosted session replay (with the P2 caveats).
Session replay promises the click sequence that broke the checkout flow. The reality is a stream of DOM mutation events that a player reconstructs in a browser. This guide covers what replay is, when it earns its bandwidth cost, and — directly — where urgentry’s replay support stands today: event ingest works, the timeline UI does not.
20 seconds. Session replay captures DOM mutations and user interactions as structured JSON events using rrweb. A player re-applies those events against a blank document to reconstruct the session. No video is recorded. The Sentry SDK ships rrweb integration out of the box. The replay payload is 200 KB to 1 MB per minute of session time.
60 seconds. Replay is valuable for a narrow class of bugs: intermittent UI regressions you cannot reproduce from error data alone. The click sequence, the scroll position, the exact state of a form before a crash. For the majority of error investigations, a stack trace, breadcrumbs, and a URL tell you what you need. Replay adds meaningful diagnostic value only when the reproduction path is the unknown. The bandwidth and storage cost is real. A 10% sample rate across 1000 daily active users generates around 25 GB of replay payload per month. Budget for it before enabling it.
Where urgentry sits today. urgentry accepts rrweb-format replay events from the Sentry SDK and stores them. That is the working part. The playback UI — the video timeline, canvas scrubbing, and the event overlay that makes replay useful — is classified as a P2 priority and is not complete as of May 2026. Canvas elements and iframes have known gaps even in the ingest path. If full replay is a hard requirement today, use OpenReplay or PostHog and link the session to urgentry via a shared session_id. This guide tells you exactly how.
What session replay actually is
Session replay does not record video. No screen capture happens at the browser level. Instead, the rrweb library instruments the DOM: it captures a full snapshot of the document on session start, then records every mutation event that follows. A user clicks a button — the click coordinates and the target element ID become an event. A React component re-renders and swaps a class name — the DOM mutation becomes an event. A network request fires — that too, if the XHR hook is enabled.
Each event is a small JSON object with a timestamp, an event type, and a payload describing what changed. The stream of events over a 5-minute session weighs 200 KB to 1 MB compressed. The replay player (also part of rrweb) re-applies that stream against a sandboxed, blank document at playback speed, reconstructing what the user saw without ever storing a pixel of video.
This architecture has a few practical implications. First, replay captures what the DOM contained, not what the screen rendered. Canvas elements, WebGL surfaces, and video frames are not part of the DOM mutation model, so they appear as blank rectangles in replay. This is a fundamental limit of the approach, not a product gap. Second, replay events are structured data, which means they are filterable, searchable, and can be correlated with error events by timestamp. Third, the Sentry SDK ships rrweb integration and sends the events to whatever DSN you configure. Point it at urgentry, and urgentry receives the events.
The distinction from video recording matters operationally. Video at 30 fps for a 5-minute session produces 50 to 200 MB of data. rrweb produces 200 KB to 1 MB. The storage and bandwidth math is dramatically different. Replay is practical at modest scale because it is event-based, not pixel-based.
When replay earns its bandwidth cost
The canonical use case is the bug you cannot reproduce from the error data alone.
An intermittent crash in a multi-step checkout flow. The stack trace points to a payment SDK callback. The error happens for 3% of checkout attempts. You cannot trigger it in your local environment. The breadcrumbs show the user was on step 3 when it fired. That tells you where, but not why. Replay shows you the exact sequence: the user filled the card number field, tabbed to the expiry, then clicked back to edit the card number without completing the expiry. The SDK expected the expiry field to be touched before the card number was re-edited. The bug is a state machine flaw that a unit test never caught because the sequence was non-obvious.
Replay surfaces that sequence in under a minute of watching. Without replay, reproducing it might take hours of reading SDK source and guessing interaction patterns.
Other situations where replay pays for itself: rage-click patterns (users clicking on an element that they expect to be interactive but is not), form abandonment before a validation error fires, UI state that looks correct in tests but breaks at a specific viewport width or with a specific browser extension active.
These are all situations where the error event or the stack trace is not the complete picture. The interaction path is the missing variable.
When replay is overkill
Most error investigations do not need replay.
A TypeError: Cannot read properties of null on a specific component? The stack trace, the component name, and the props at the time of the error are what you need. A failed API request returning a 500? The request URL, the response body from the breadcrumb, and the trace context link you to the server-side error. A crash on a specific browser version? The user agent string from the event context narrows it immediately.
In all of these cases, replay adds data volume without adding diagnostic signal. The cause is visible from the error data. Adding replay to these investigations introduces noise: you spend time watching a session replay that confirms what the stack trace already told you.
The heuristic: reach for replay when you have the error event but the reproduction path is unknown. If the error event contains enough information to reproduce the bug, replay is not needed. A 10% sample rate is the right default for most applications, not 100%, because most sessions produce errors that the error data explains fully.
The privacy and bandwidth cost
Replay captures the DOM. The DOM contains user input. Without masking, replay records everything a user types into every form field: passwords, credit card numbers, personal information, search queries. This is not a theoretical risk; it is the default behavior of rrweb without explicit configuration.
Input masking is required before you enable replay on any form that handles personal data. The Sentry SDK ships two mechanisms:
data-sentry-maskon any element masks its text content and input values in the replay event stream. The element appears as a gray block in the player.data-sentry-blockon an element blocks it from the event stream entirely: the element and its children produce no events and appear as a solid rectangle in replay.
A conservative default: mask all <input>, <textarea>, and <select> elements by class or by the SDK’s maskAllInputs option. Add exceptions for elements that are safe to capture. This is the safer starting posture than the inverse.
The bandwidth arithmetic: at a 10% sample rate, 1000 daily active users, and a 5-minute average session length, you generate 500 sessions per day. At 500 KB per session (mid-range of the 200 KB to 1 MB range), that is 250 MB per day, or about 7.5 GB per month. At the high end of 1 MB per session, it is 500 MB per day and 15 GB per month. These are stored payloads before any replication. The back-of-envelope calculation in the storage section below uses slightly higher numbers based on heavier SPAs with frequent DOM mutations.
urgentry’s replay status today
urgentry accepts replay events from the Sentry SDK. When you initialize the Sentry SDK with replay enabled and point the DSN at urgentry, the SDK sends rrweb event segments to urgentry’s envelope endpoint alongside error events and transactions. urgentry stores these segments.
That is the working part. Here is what is not working:
- The playback UI. urgentry does not have a functional video timeline and replay player as of May 2026. The stored events cannot be played back from the urgentry interface. The timeline scrubber, the event overlay, and the frame-by-frame navigation that make replay useful for diagnosis do not exist yet in urgentry’s UI. This is a P2 roadmap item, not a P1.
- Canvas and iframe capture. rrweb has optional canvas serialization, which serializes canvas content as image data and attaches it to the event stream. urgentry’s ingest does not fully handle canvas serialization payloads. Sessions that rely heavily on canvas rendering will have gaps in what is stored. Iframes in cross-origin configurations have similar known gaps at the ingest layer.
- Session search and filtering. The stored replay segments are not indexed for session-level search in urgentry’s current UI. You cannot filter sessions by error type, by user ID, or by URL pattern from within urgentry today.
What this means practically: do not configure the Sentry SDK to send replay events to urgentry expecting a functional replay UI. The events will arrive and be stored, but you will not be able to play them back or navigate them. If you enable replay in the SDK today and point at urgentry, you will generate replay storage without getting replay diagnostic value. That is a cost with no benefit.
The honest recommendation: use urgentry for error tracking and traces. Use a dedicated replay tool for replay. The pattern that works today is in the next section.
The full-replay alternatives
Three self-hostable replay tools cover the space that urgentry does not yet address.
OpenReplay
OpenReplay is fully open source under the ELv2 license. The self-hosted edition is free. The architecture runs as a set of Docker containers: an ingest service, a storage service (MinIO or S3-compatible), and a frontend. The rrweb-based SDK captures DOM events, network requests, console logs, and Redux state changes. The player UI includes a timeline, an event sidebar, a network waterfall, and console output. Operationally, it requires around 4 GB of RAM for the full stack and grows storage proportionally to session volume. The license permits self-hosting without restriction but limits some enterprise features to the paid cloud tier.
For teams running urgentry for errors and needing a self-hosted replay option today, OpenReplay is the closest match on cost and operationally.
PostHog session replay
PostHog’s session replay is part of the PostHog product suite (feature flags, analytics, replay). The self-hosted edition is open source under MIT for the core and AGPL for some services. Replay is available in the self-hosted edition. The PostHog stack is heavier than a standalone replay tool: it requires ClickHouse for analytics storage. If you already run PostHog for product analytics, replay is an included feature. If you do not run PostHog, deploying it for replay alone is significant operational overhead. PostHog’s replay player is full-featured: timeline, DOM inspector, network requests, console, and rage-click detection.
Highlight.io
Highlight.io is open source under the Apache 2.0 license. It combines session replay with error monitoring and logging in a single product. The self-hosted deployment uses Docker Compose. The replay feature is well-developed and includes the timeline UI, console, network activity, and user session metadata. The error monitoring component overlaps with urgentry’s feature set, so running both creates redundancy. Highlight is a better fit for teams that want replay as the primary tool and are willing to consolidate error monitoring into it as well.
| Tool | License | Self-hosted cost | Replay UI | Min. RAM (self-hosted) |
|---|---|---|---|---|
| OpenReplay | ELv2 | Free | Full (timeline, events, network) | 4 GB |
| PostHog replay | MIT / AGPL | Free (requires ClickHouse) | Full | 8 GB |
| Highlight.io | Apache 2.0 | Free | Full | 4 GB |
| urgentry (replay) | FSL-1.1-Apache-2.0 | Free | Partial (ingest only, P2) | 256 MB |
A pattern that works today
Run urgentry for error tracking and traces. Run OpenReplay for session replay. Link the two via a shared session_id. This is the pattern that gives you full functionality from both tools without waiting for urgentry’s P2 replay UI.
Step 1: initialize both SDKs
In your frontend application, initialize the Sentry SDK pointed at urgentry (without the replay integration), and initialize the OpenReplay tracker separately:
import * as Sentry from "@sentry/browser";
import OpenReplay from "@openreplay/tracker";
// urgentry for errors
Sentry.init({
dsn: "https://key@urgentry.example.com/1",
release: "my-app@2.1.0",
environment: "production",
// do NOT add replayIntegration() here
// urgentry's replay UI is not ready
});
// OpenReplay for session replay
const tracker = new OpenReplay({
projectKey: "your-openreplay-project-key",
ingestPoint: "https://openreplay.example.com/ingest",
obscureInputEmails: true,
obscureInputNumbers: true,
obscureTextEmails: true,
});
tracker.start();
Step 2: share the session_id
After OpenReplay starts, it exposes the session ID for the current session. Attach that ID to every Sentry event via the scope:
tracker.start().then(() => {
const sessionId = tracker.getSessionID();
Sentry.setTag("openreplay_session_id", sessionId);
Sentry.setContext("replay", {
session_id: sessionId,
session_url: tracker.getSessionURL(),
});
});
Every error that urgentry receives from this point forward carries the openreplay_session_id tag and the direct session URL. When you investigate an error in urgentry, the session URL is in the event context. One click opens the OpenReplay player at the right session.
Step 3: add release context
Tag the OpenReplay session with the same release identifier that urgentry receives:
tracker.setMetadata("release", "my-app@2.1.0");
tracker.setMetadata("environment", "production");
With both tools sharing release and session_id, you can navigate in either direction: from an urgentry error to the OpenReplay session, or from an OpenReplay session with a rage-click to the urgentry error that fired at the same timestamp.
In urgentry: open the error event, find the replay.session_url in event context, follow the link to OpenReplay. In OpenReplay: open a session, find the console error at the timestamp of the issue, search urgentry for errors in the same release and time window. The two tools do not integrate natively; the session URL and the release tag are the bridge.
Privacy controls
Input masking is not optional if your application handles personal data. Configure it before enabling replay in any environment, not after.
The Sentry SDK (when you eventually use it for replay or for the masking attribute conventions) respects two element-level attributes:
data-sentry-maskreplaces the element’s text content with a placeholder in the event stream. Form values, labels, and inner text are masked.data-sentry-blockremoves the element from the event stream entirely. Use this for payment iframes, SSN fields, or any element whose presence in the event data creates compliance risk.
OpenReplay ships equivalent controls: data-openreplay-obscured masks the element, and you can configure class-based masking at the tracker initialization level. The SDK-level option obscureInputEmails: true and obscureInputNumbers: true mask all email and numeric inputs across the application without requiring per-element attributes.
On the consent question: in EU jurisdictions, capturing session replay without user consent is a GDPR risk. The common approach is to activate the tracker inside the consent callback rather than at page load, or to restrict replay to authenticated sessions covered by your terms of service. Consult your legal team before enabling replay in production for EU users. This is not a tooling question; it is a legal question that the tooling cannot answer for you.
Storage cost
Replay storage grows fast. The back-of-envelope for a moderately active SPA:
- 1000 daily active users
- 10% replay sample rate (100 sessions per day)
- Average session length: 5 minutes
- Payload per session: 500 KB (mid-range for a React SPA with frequent state changes)
That produces 50 MB per day of raw replay payload, or about 1.5 GB per month. At the high end (1 MB per session, 15% sample rate, 8-minute sessions), the same 1000 DAU generates roughly 36 GB per month. A heavier SPA with canvas elements or frequent DOM mutations trends toward the high end.
The back-of-envelope for a larger application: 10,000 DAU at 10% sample rate, 5-minute average session, 500 KB per session produces 500 MB per day and 15 GB per month. At that scale, replay storage is a line item you need to budget explicitly. S3 storage costs are modest (approximately $0.02 per GB per month on AWS), but egress and replication multiply the effective cost.
Set a retention policy. Replay data older than 30 days has low diagnostic value for most teams. Configure your object storage lifecycle policy to delete replay segments after 30 to 90 days. For compliance-sensitive applications, the retention period may be set by legal requirements rather than operational preference.
Frequently asked questions
Does urgentry support session replay?
Partially. urgentry ingests rrweb-format replay events from the Sentry SDK and stores them. The playback UI (the video timeline and canvas controls) is a P2 roadmap item and is not complete as of May 2026. If full replay playback is a requirement today, use OpenReplay or PostHog alongside urgentry and link the two tools via a shared session_id.
What is rrweb and how does replay work under the hood?
rrweb is an open-source library that captures DOM mutation events, user interactions, and network activity as structured JSON records. On session start, it serializes a full snapshot of the document. From that point, every DOM change becomes a small JSON event. The replay player re-applies those events against a blank document at playback time to reconstruct what the user saw. No video is recorded. Canvas elements, WebGL, and video content appear as blank rectangles because they fall outside the DOM mutation model.
How much bandwidth does session replay consume?
A typical rrweb session on a React SPA produces 200 KB to 1 MB of event data per minute of session time. At 1000 daily active users, a 10% sample rate, and a 5-minute average session, that is approximately 50 MB per day or 1.5 GB per month at the mid-range payload size. Heavier SPAs with frequent DOM mutations or canvas content trend toward the high end.
Do I need a consent prompt for session replay?
That depends on your jurisdiction. In EU jurisdictions under GDPR, capturing replay data without user consent is a compliance risk. The standard approach is to activate the replay tracker inside a consent callback, or to restrict replay to authenticated sessions where your terms of service covers the data collection. This is a legal question, not a tooling question. Consult legal counsel before enabling replay for EU users.
Can I link an urgentry error to a replay session in OpenReplay?
Yes. Read the session ID from the OpenReplay tracker after it starts and set it as a tag on the Sentry scope (Sentry.setTag("openreplay_session_id", sessionId)). Every urgentry error from that point forward carries the session ID. When you open the error in urgentry, the session URL in the event context links directly to the OpenReplay player. The cross-tool navigation requires a manual click today; there is no native integration between the two tools.
Sources and further reading
- rrweb GitHub repository — the open-source DOM recording library that underlies Sentry replay, OpenReplay, and most other session replay tools. The README covers event types, plugin architecture, and the canvas serialization approach.
- OpenReplay self-hosted deployment docs — Docker Compose and Kubernetes deployment guides for OpenReplay; covers storage configuration, MinIO integration, and SSL setup.
- PostHog session replay documentation — SDK configuration, privacy controls, sampling rate options, and the ClickHouse storage requirements for the self-hosted edition.
- Highlight.io self-hosted guide — Docker Compose quickstart for the Highlight.io stack, which includes replay, error monitoring, and logging in a single deployment.
- FSL-1.1-Apache-2.0 license text — the source-available license under which urgentry is distributed. Permits self-hosting and modification; read Section 2 for the commercial use boundary.
- urgentry compatibility matrix — the full account of urgentry’s Sentry SDK feature coverage, including the replay P2 status, canvas gaps, and the ingest endpoints that are fully supported.
Error tracking that tells the truth about what it does.
urgentry handles errors, traces, and error grouping in one binary at 52 MB resident. Replay is partial today. If you need both, pair urgentry with OpenReplay and link them via session_id. The combination covers the full observability surface without waiting for a roadmap item.