Self-hosting
Self-Hosting AnyReplay: Overview and Requirements
What a self-hosted AnyReplay deployment looks like: the services, host requirements, storage sizing, TLS, backups and how the SDKs point at your own ingest.
AnyReplay can run on infrastructure you control, so recordings never leave it. Self-hosted ingest is available on the Enterprise plan — talk to us about running AnyReplay on your own infrastructure. This page summarises what the deployment guide that ships with the source (deploy/README.md) asks for, so you can judge the effort before you start.
The shape of a deployment
The reference deployment runs everything on one host behind nginx. The split is by service, so moving ingest to its own machine later is a move, not a rewrite.
| Component | What it does | Reference port |
|---|---|---|
| Dashboard (Next.js) | The web app your team signs in to | 7100 |
| API (NestJS) | Accounts, projects, sessions, translation, retention sweeps | 7101 |
| Ingest (Fastify) | Receives recordings from the SDKs; also serves the recorder bundle | 7102 |
| PostgreSQL | Everything except the recordings themselves | 5432 |
| Chunk storage | The recordings, on the filesystem | — |
Ingest is a separate service because its load is the opposite of the dashboard's: high-volume, unauthenticated writes from every visitor of every site you record. It holds no JWT secrets and no translation key.
Requirements
- Operating system: the guide is written for Ubuntu 24.04.
- Node.js 20.19 or newer (the guide installs a private Node 22 so other applications on the host are untouched) and pnpm 10.
- PostgreSQL, with an ordinary, non-superuser role and its own database. The schema is created on first boot; there is no separate migration step.
- nginx in front of the three services, with TLS. The reference setup sits behind Cloudflare with an Origin CA certificate.
- Memory for builds: the Next.js build wants roughly 2 GB. Add swap if the host is tight.
- Disk for recordings: a session is roughly 100–500 KB, so ten thousand live sessions is a few gigabytes. Retention sweeps keep it from growing without bound.
Hostnames
| Hostname | Routed to |
|---|---|
Dashboard, e.g. replay.example.com | Dashboard |
API, e.g. api.replay.example.com | API |
Ingest, e.g. in.replay.example.com | Ingest — this is your ingestUrl |
Recorder, e.g. cdn.replay.example.com | Ingest, which serves /v1/ar.min.js |
Pointing the SDKs at it
anyreplay('init', {
projectKey: 'YOUR_PROJECT_KEY',
ingestUrl: 'https://in.replay.example.com',
});createRecorder({
projectKey: 'YOUR_PROJECT_KEY',
ingestUrl: 'https://in.replay.example.com',
});init({
projectKey: 'YOUR_PROJECT_KEY',
ingestUrl: 'https://in.replay.example.com',
});The dashboard's setup page generates snippets with your own ingest and recorder URLs, as long as they were set when the dashboard was built — see configuration. Remember to list those hosts in your Content Security Policy.
Operations the guide covers
- Services: systemd units for the three services, installed and restarted by the deployment workflow.
- Visitor IP and firewall: real client addresses from the proxy, so ingest's per-address rate limit means something, and an origin that only accepts traffic from the proxy.
- Backups: a script that dumps PostgreSQL and the recordings, encrypts them on the host and uploads one file per run to an off-site bucket, with a weekly job that restores the latest backup into a throwaway database to prove it works.
- Deployments: a CI workflow that runs the test suite, builds on the host, restarts the services and checks each health endpoint.
- Health:
GET /healthon the API and on ingest.
Same code, same defaults
A self-hosted deployment runs the same code as the hosted service: the same masking, including the fields that are never recorded whatever you configure, the allowed-domain check that makes a public key safe, the sandboxed replay iframe, retention sweeps and the audit trail. What changes is who holds the keys and the disk. For the reasoning behind self-hosting, see self-hosted session replay.