Self-hosting
Run RelayRTC locally on your computer with Docker.
Requirements
Run the API, console, signaling, media, PostgreSQL and TURN on your own computer. This setup is for developing and testing applications on localhost.
- Docker Desktop with Linux containers on Windows or macOS, or Docker Engine with the Compose plugin on Linux.
- Git.
- Node.js 22 or newer for the secret-generation commands below.
Start Docker, then check it is available:
docker version
docker compose versionThe application dependencies are installed inside the images. You do not need Go or pnpm on your computer for this setup.
Download RelayRTC
git clone https://github.com/relayrtc/relayrtc.git
cd relayrtcCopy the environment template. In PowerShell:
Copy-Item .env.example .envIn Git Bash, macOS or Linux:
cp .env.example .envOpen .env in your editor. Run the remaining commands from the repository root.
Generate secrets
Generate a new value for each setting in this table:
node -e "console.log(require('node:crypto').randomBytes(32).toString('hex'))"| Setting | Used for |
|---|---|
POSTGRES_PASSWORD | PostgreSQL authentication. |
BETTER_AUTH_SECRET | Console authentication. |
PARTICIPANT_TOKEN_SIGNING_SECRET | Participant tokens. |
RELAYRTC_INTERNAL_SECRET | Communication between services. |
TURN_SHARED_SECRET | Temporary TURN credentials. |
GRAFANA_ADMIN_PASSWORD | Grafana login if you start monitoring. |
Paste each value into its setting in .env. Use separate values for the authentication, participant token, internal and TURN secrets. The containers run in production mode and reject placeholder or shared credentials, even on your computer.
Update DATABASE_URL with the same PostgreSQL password:
DATABASE_URL=postgresql://relaykit:YOUR_POSTGRES_PASSWORD@localhost:5433/relaykitCompose supplies the containers with a database URL using the internal postgres hostname. The URL above is for tools running on your computer.
Generate the webhook encryption key:
node -e "console.log(require('node:crypto').randomBytes(32).toString('base64'))"Save that output as WEBHOOK_SIGNING_ENCRYPTION_KEY. It requires a base64-encoded 32-byte key, not a hexadecimal string.
Do not commit .env or put its secrets in frontend code. Keep the webhook encryption key if you want to reuse your database; existing webhook signing secrets depend on it.
Configure localhost
Use these values in .env:
API_PORT=8080
SIGNALING_PORT=8081
MEDIA_PORT=8083
CONSOLE_PORT=3002
POSTGRES_PORT=5433
BETTER_AUTH_URL=http://localhost:3002
CONSOLE_AUTH_URL=http://localhost:3002
BETTER_AUTH_TRUSTED_ORIGINS=http://localhost:3002
RELAYRTC_API_URL=http://localhost:8080
RELAYRTC_SIGNALING_ALLOWED_ORIGINS=http://localhost:3005,http://localhost:3002
MEDIA_RTC_LISTEN_IP=0.0.0.0
MEDIA_RTC_ANNOUNCED_ADDRESS=127.0.0.1
MEDIA_RTC_PORT=40000
MEDIA_RTC_MAX_PORT=40003
MEDIA_WORKERS=1
TURN_REALM=relayrtc.local
TURN_PUBLIC_IP=127.0.0.1
TURN_DOCKER_RELAY_ADDRESS=172.30.0.11
TURN_STUN_URLS=stun:localhost:3478
TURN_URLS=turn:localhost:3478?transport=udp,turn:localhost:3478?transport=tcpThis example expects your frontend at http://localhost:3005. Change the allowed origins to match your application's exact origin, including its port. http://127.0.0.1:3005 and http://localhost:3005 are different origins.
Keep TURN_DOCKER_RELAY_ADDRESS=172.30.0.11. It is the coturn container's address in the Compose network.
The image includes a self-signed TURN TLS certificate. The URLs above use TURN over UDP and TCP, so you do not need to configure a trusted certificate for this setup.
Configure console sign-in
Replace the provider placeholders in .env:
GITHUB_CLIENT_IDandGITHUB_CLIENT_SECRET.GOOGLE_CLIENT_IDandGOOGLE_CLIENT_SECRET.RESEND_API_KEYandRESEND_FROM_EMAIL, using a verified sender.
Register this callback URL in your GitHub OAuth application:
http://localhost:3002/api/auth/callback/githubRegister this callback URL in your Google OAuth application:
http://localhost:3002/api/auth/callback/googleThe current console configuration requires both OAuth providers and the email settings. It also supports email and password sign-in. Provider placeholders do not give you working OAuth or email delivery.
Start RelayRTC
docker compose --env-file .env config --quiet
docker compose --env-file .env up --detach --build --wait postgres coturn signaling media api console
docker compose psThe first build takes longer because Docker downloads images and installs dependencies.
The migrate service applies the database migrations before the application services start. An exit code of 0 for that container means the migrations completed.
Local endpoints
| Service | Address |
|---|---|
| Console | http://localhost:3002 |
| REST API | http://localhost:8080/v1 |
| Signaling | ws://localhost:8081/v1/connect |
| Media readiness | http://localhost:8083/ready |
| PostgreSQL | localhost:5433 |
| STUN and TURN | localhost:3478 |
The media HTTP address is an internal control service, not an SDK endpoint. Browsers receive media transport information through signaling.
Open the console, create your account, organization, project and environment, then create a secret API key.
Connect your application
Set these values on your application's backend:
RELAYRTC_API_URL=http://localhost:8080/v1
RELAYRTC_SIGNALING_URL=ws://localhost:8081/v1/connect
RELAYRTC_SECRET_KEY=your-project-secret-keyFollow Build your first call to create a room and issue participant tokens. Use the API URL above for this deployment; the quickstart's example port may differ.
Keep the secret key on your backend. Your frontend receives a participant token and the signaling URL.
Open two browser windows and join the same room with different participant tokens. Allow camera and microphone access, then test audio, video, messages and screen sharing. Browsers allow camera and microphone access on localhost without configuring HTTPS.
Troubleshooting
Inspect the logs:
docker compose logs --tail 100 api signaling media coturn console migrate| Problem | Check |
|---|---|
| Docker cannot connect | Docker Desktop or the Docker Engine service is running. |
| A port is already in use | Stop the other application or change the host port in .env. |
| API exits during startup | Replace placeholder secrets and check the webhook key's base64 encoding. |
| PostgreSQL rejects the password | An existing database volume keeps its original password. Use that password or update it in PostgreSQL. |
| Signaling rejects the connection | Match your frontend origin and port in RELAYRTC_SIGNALING_ALLOWED_ORIGINS. |
| Camera or microphone is unavailable | Check browser permissions and whether another application is using the device. |
| Room joins but remote media is missing | Check the media address, Docker port mappings and your computer's firewall. |
| Console OAuth or email fails | Check provider credentials, callback URLs and the Resend sender. |
See Troubleshooting calls for browser checks.
This configuration is for browsers running on the same computer as Docker. A phone or another computer cannot reach it through localhost. Testing on another device requires reachable addresses, firewall access and HTTPS for browser camera and microphone permissions.
Apply changes
After editing .env, recreate the services so they receive the new values:
docker compose --env-file .env up --detach --wait postgres coturn signaling media api consoleAfter updating the repository, rebuild the images and run the migrations:
git pull --ff-only
docker compose --env-file .env build --pull
docker compose --env-file .env run --rm migrate
docker compose --env-file .env up --detach --wait postgres coturn signaling media api consoleStop if migration fails. Updating media or signaling can interrupt active calls.
Stop or reset
Stop the stack and keep its saved data:
docker compose downTo delete the local database and other Compose volumes:
docker compose down --volumesRemoving volumes deletes accounts, projects, API keys, rooms and other stored records. Export any data you need before resetting.