Docs
Deploy

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:

Shell
docker version
docker compose version

The application dependencies are installed inside the images. You do not need Go or pnpm on your computer for this setup.

Download RelayRTC

Shell
git clone https://github.com/relayrtc/relayrtc.git
cd relayrtc

Copy the environment template. In PowerShell:

powershell
Copy-Item .env.example .env

In Git Bash, macOS or Linux:

Shell
cp .env.example .env

Open .env in your editor. Run the remaining commands from the repository root.

Generate secrets

Generate a new value for each setting in this table:

Shell
node -e "console.log(require('node:crypto').randomBytes(32).toString('hex'))"
SettingUsed for
POSTGRES_PASSWORDPostgreSQL authentication.
BETTER_AUTH_SECRETConsole authentication.
PARTICIPANT_TOKEN_SIGNING_SECRETParticipant tokens.
RELAYRTC_INTERNAL_SECRETCommunication between services.
TURN_SHARED_SECRETTemporary TURN credentials.
GRAFANA_ADMIN_PASSWORDGrafana 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:

Shell
DATABASE_URL=postgresql://relaykit:YOUR_POSTGRES_PASSWORD@localhost:5433/relaykit

Compose 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:

Shell
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.

Keep your environment file private

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:

Shell
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=tcp

This 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_ID and GITHUB_CLIENT_SECRET.
  • GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET.
  • RESEND_API_KEY and RESEND_FROM_EMAIL, using a verified sender.

Register this callback URL in your GitHub OAuth application:

text
http://localhost:3002/api/auth/callback/github

Register this callback URL in your Google OAuth application:

text
http://localhost:3002/api/auth/callback/google

The 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

Shell
docker compose --env-file .env config --quiet
docker compose --env-file .env up --detach --build --wait postgres coturn signaling media api console
docker compose ps

The 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

ServiceAddress
Consolehttp://localhost:3002
REST APIhttp://localhost:8080/v1
Signalingws://localhost:8081/v1/connect
Media readinesshttp://localhost:8083/ready
PostgreSQLlocalhost:5433
STUN and TURNlocalhost: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:

Shell
RELAYRTC_API_URL=http://localhost:8080/v1
RELAYRTC_SIGNALING_URL=ws://localhost:8081/v1/connect
RELAYRTC_SECRET_KEY=your-project-secret-key

Follow 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:

Shell
docker compose logs --tail 100 api signaling media coturn console migrate
ProblemCheck
Docker cannot connectDocker Desktop or the Docker Engine service is running.
A port is already in useStop the other application or change the host port in .env.
API exits during startupReplace placeholder secrets and check the webhook key's base64 encoding.
PostgreSQL rejects the passwordAn existing database volume keeps its original password. Use that password or update it in PostgreSQL.
Signaling rejects the connectionMatch your frontend origin and port in RELAYRTC_SIGNALING_ALLOWED_ORIGINS.
Camera or microphone is unavailableCheck browser permissions and whether another application is using the device.
Room joins but remote media is missingCheck the media address, Docker port mappings and your computer's firewall.
Console OAuth or email failsCheck provider credentials, callback URLs and the Resend sender.

See Troubleshooting calls for browser checks.

Localhost means this computer

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:

Shell
docker compose --env-file .env up --detach --wait postgres coturn signaling media api console

After updating the repository, rebuild the images and run the migrations:

Shell
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 console

Stop if migration fails. Updating media or signaling can interrupt active calls.

Stop or reset

Stop the stack and keep its saved data:

Shell
docker compose down

To delete the local database and other Compose volumes:

Shell
docker compose down --volumes
Reset deletes your local data

Removing volumes deletes accounts, projects, API keys, rooms and other stored records. Export any data you need before resetting.