Self-Hosting

Docs

Self-Hosted Deployment

Deploy Rejourney on your own infrastructure for self-hosted mobile analytics, session replay, crash reporting, and observability.

Use AI to deploy self-hosted

Using Cursor, Claude, or ChatGPT? Copy the full self-hosted deployment prompt to turn this guide into a server-specific checklist for DNS, Docker, storage, SDK setup, backups, and troubleshooting.

Self-hosting Rejourney

This guide walks you through running Rejourney on your own server with the official single-node Docker Compose stack. The default storage path is built-in MinIO. You can choose external S3-compatible storage during setup if you already have a bucket.

You do not need Kubernetes, Rejourney cloud infrastructure, or a complicated multi-node setup. You need one server, one domain, Docker, and a little patience while images pull.

After setup you get:

  • A dashboard at your domain with HTTPS from Let's Encrypt.
  • An API host for the dashboard and SDKs.
  • An ingest upload relay host for replay bytes.
  • Postgres, Redis, and either built-in MinIO or your own S3-compatible storage.
  • Background workers for ingest, replay processing, retention, lifecycle, and alerts.

All commands below assume you are in the repository root, the folder that contains docker-compose.selfhosted.yml.


Quick Path

Use this when you are doing a fresh install on a VPS.

git clone https://github.com/rejourneyco/rejourney.git
cd rejourney
./scripts/selfhosted/deploy.sh install

The installer asks for your domain, Let's Encrypt email, and storage choice. It creates .env.selfhosted, starts the stack, runs database bootstrap, and prints your URLs.

[!IMPORTANT] Save .env.selfhosted somewhere secure right after install. It contains all deployment secrets, including the key used to decrypt stored storage credentials.


Before You Start

Use this checklist before running the installer.

  • I have a Linux server that can run Docker.
  • docker compose version works on the server.
  • Ports 80 and 443 are reachable from the internet.
  • I control a base domain, for example example.com.
  • I know which storage path I want: built-in MinIO or external S3-compatible storage.
  • I have an email address for Let's Encrypt certificate notices.
  • I have a safe place to store .env.selfhosted.
ItemRecommendation
OSUbuntu 22.04+, Debian 12+, or another Docker-friendly Linux
DockerDocker 24+ with the Docker Compose plugin
CPU/RAM4 vCPU and 8 GB RAM
Disk40 GB minimum; more if you keep many recordings
NetworkPublic inbound 80 and 443

Domain worksheet

Replace example.com with your real domain and point each hostname at the server public IP.

HostnamePurposeDNS record
example.comDashboardA or AAAA to your server
www.example.comRedirects to dashboardA or AAAA to your server
api.example.comAPI and SDK configA or AAAA to your server
ingest.example.comUpload relayA or AAAA to your server

DNS can take a few minutes to settle. Let's Encrypt certificates will not issue until these names resolve to the machine running Rejourney.


Choose Storage

The installer asks which storage backend to use.

Choose thisBest forWhat you provide
Built-in MinIOMost single-server installsNothing extra; the installer generates MinIO credentials
External S3-compatible storageTeams that already operate object storageEndpoint, bucket, region, access key, and secret key

Built-in MinIO

Built-in MinIO is the default and recommended path for a simple VPS. It runs inside Docker and is not exposed publicly by default. Session bytes are uploaded to your Rejourney ingest relay first, then written server-side to MinIO.

External S3-compatible storage

Use AWS S3, Cloudflare R2, Hetzner Object Storage, Wasabi, or any S3-compatible API. The important network path is:

ingest-upload container -> your S3 endpoint

Browsers and phones do not need direct write access to the bucket.

Common endpoint shapes:

ProviderEndpoint example
AWS S3https://s3.<region>.amazonaws.com
Cloudflare R2https://<account-id>.r2.cloudflarestorage.com
Hetznerhttps://<location>.your-objectstorage.com

If your provider gives you a separate public download URL, set S3_PUBLIC_ENDPOINT in .env.selfhosted and run ./scripts/selfhosted/deploy.sh update.


Install

1. Clone Rejourney

git clone https://github.com/rejourneyco/rejourney.git
cd rejourney

Stay on the default branch unless the project documents a release tag for self-hosting.

2. Run the installer

./scripts/selfhosted/deploy.sh install

The installer will:

  1. Ask for your base domain, for example example.com.
  2. Ask for your Let's Encrypt email.
  3. Ask whether to use built-in MinIO or external S3-compatible storage.
  4. Create .env.selfhosted with generated passwords and secrets.
  5. Pull published application images.
  6. Build the bootstrap image from your local checkout.
  7. Start Postgres, Redis, Traefik, and MinIO when selected.
  8. Validate database connectivity before bootstrap.
  9. Apply the database schema, seed first-time system data, and configure storage.
  10. Start the API, upload relay, web dashboard, and workers.

First install can take several minutes.

3. Save the generated config

ls -l .env.selfhosted

Back up .env.selfhosted in a password manager or encrypted backup. Do not commit it.


Verify The Install

Run:

./scripts/selfhosted/deploy.sh status

You want to see:

  • api running and healthy.
  • ingest-upload running and healthy.
  • web running.
  • Postgres and Redis healthy.
  • MinIO running if you chose built-in MinIO.
  • Worker services running.

The installer prints your URLs:

ServiceURL shape
Dashboardhttps://example.com
APIhttps://api.example.com
Ingest upload relayhttps://ingest.example.com
WWW redirecthttps://www.example.com -> https://example.com

Health checks

From the server, you can also check:

curl -fsS https://api.example.com/health
curl -fsS https://api.example.com/health/ingest

Replace example.com with your domain.

First recording

  • Open the dashboard.
  • Create an account.
  • Create a project.
  • Add the SDK to a test app.
  • Record a short session.
  • Confirm the session appears in Replay.

If sessions are counted but Replay is empty, check Troubleshooting.


Configure Your Apps

Point SDKs at your API host. The API host must match PUBLIC_API_URL / API_DOMAIN.

React Native

import { initRejourney, startRejourney } from '@rejourneyco/react-native';
initRejourney('rj_your_public_key', {
apiUrl: 'https://api.example.com',
});
startRejourney();

Upload URLs are derived from server config when PUBLIC_INGEST_URL is correct.

Swift iOS

After adding the Rejourney Swift package, configure the SDK with your self-hosted API URL.

import SwiftUI
import Rejourney
@main
struct MyApp: App {
@MainActor
init() {
Rejourney.configure(
publicKey: "rj_your_public_key",
options: RejourneyOptions(
apiURL: URL(string: "https://api.example.com")!
)
)
Task { await Rejourney.start() }
}
var body: some Scene {
WindowGroup {
ContentView()
}
}
}

Swift reads project recording settings from PUBLIC_API_URL and uses the server-provided upload relay configuration. If you use UIApplicationDelegate, call the same Rejourney.configure(...) before Rejourney.start().

Web SDK

import { initRejourney, startRejourney } from '@rejourneyco/browser';
await initRejourney('rj_your_public_key', {
apiUrl: 'https://api.example.com',
});
await startRejourney();

For browser apps, also add your app origin to the project Web allowed domains. If your app runs at https://app.example.com, allow app.example.com. For local testing, include the port, for example localhost:3100.

The Web SDK uses:

  • PUBLIC_API_URL for config, device auth, and ingest coordination.
  • PUBLIC_INGEST_URL for signed upload relay URLs.

Both hosts must be reachable from the user's browser.


Daily Operations

TaskCommand
Show service status./scripts/selfhosted/deploy.sh status
Follow all logs./scripts/selfhosted/deploy.sh logs
Follow one service./scripts/selfhosted/deploy.sh logs api
Upgrade images and rerun bootstrap./scripts/selfhosted/deploy.sh update
Stop services without deleting data./scripts/selfhosted/deploy.sh stop
Reset containers and volumes./scripts/selfhosted/deploy.sh reset

[!CAUTION] reset is destructive. It removes self-hosted containers and Docker volumes, including Postgres and MinIO data. Use it only when you want a clean install and old data can be deleted.

Updating safely

Use:

./scripts/selfhosted/deploy.sh update

update pulls newer images, rebuilds the bootstrap image from your checkout, restarts services, and reruns schema/storage bootstrap. It does not wipe Postgres or object storage volumes.


Important Configuration

The installer writes .env.selfhosted. These are the values you are most likely to touch later:

AreaVariables
DomainsBASE_DOMAIN, DASHBOARD_DOMAIN, API_DOMAIN, INGEST_DOMAIN
Public URLsPUBLIC_DASHBOARD_URL, PUBLIC_API_URL, PUBLIC_INGEST_URL
DatabaseDATABASE_URL, POSTGRES_*
RedisREDIS_URL, REDIS_PASSWORD
StorageSTORAGE_BACKEND, S3_*, MINIO_*
SecurityJWT_SECRET, JWT_SIGNING_KEY, INGEST_HMAC_SECRET, STORAGE_ENCRYPTION_KEY

Optional integrations can stay blank until you need them:

  • Stripe
  • SMTP
  • GitHub OAuth
  • Turnstile

After changing domain or storage settings, run:

./scripts/selfhosted/deploy.sh update

How Bootstrap Works

You normally do not need to run SQL by hand.

SituationWhat bootstrap does
Empty databaseApplies the current schema, stamps satisfied migrations, seeds system data, and configures storage
Existing initialized databaseApplies only pending migrations and refreshes safe system/storage data
Database has tables but no migration historyStops instead of guessing, to avoid damaging data

If bootstrap stops because credentials do not match existing Postgres data, restore the original .env.selfhosted and run update. Only use reset if losing the existing data is acceptable.


Apple Silicon And ARM Servers

On ARM64 machines, the deploy script sets DOCKER_DEFAULT_PLATFORM=linux/amd64 when you have not set it yourself. This helps when published images are amd64-only.

The bootstrap image is always built locally from your checkout, so database setup matches your code.


What Runs In Docker

ServiceRole
TraefikHTTPS certificates and routing
PostgresApplication data
RedisQueues, caches, and rate-limit state
MinIOBuilt-in object storage when selected
APIMain dashboard and SDK API
ingest-uploadUpload relay for replay/event bytes
webDashboard UI
ingest-workerProcesses event artifacts
replay-workerProcesses replay artifacts
session-lifecycle-workerFinalizes and reconciles sessions
retention-workerRuns scheduled retention cleanup
alert-workerSends alert-related work

Backups

At minimum, back up:

  • Postgres
  • .env.selfhosted
  • MinIO data if you use built-in MinIO

Use:

./scripts/selfhosted/backup.sh
./scripts/selfhosted/backup.sh --full

Details: Backup & Recovery.


Where To Go Next