Self-hosting Roomler
Run the whole Roomler server yourself with one compose file and a published container image — accounts, signalling, chat, conferencing and relays.
- self-hosting
- install
- docker
- getting-started
- operations
Self-hosting is a first-class path, not a stripped-down one. The same image that runs the hosted service runs on your own box, with no feature held back and no device limit imposed by us.
What you are about to run
One container with the API, the web app, the signalling plane, the chat and conferencing server and the relay floor — plus MongoDB and MinIO for state and file storage.
You need Docker with Compose v2, about 4 GB RAM and 10 GB disk.
Quickstart
git clone https://github.com/gjovanov/roomler-ai.git
cd roomler-ai
cp .env.selfhost.example .env.selfhostFill in the required values in .env.selfhost. Generate the secrets:
openssl rand -hex 32 # ROOMLER_JWT_SECRET
openssl rand -hex 32 # ROOMLER_TURN_SECRET
openssl rand -hex 24 # MONGO_ROOT_PASSWORD
openssl rand -hex 24 # MINIO_ROOT_PASSWORDKeep the datastore passwords alphanumeric
The MongoDB password is interpolated into a connection URL, so a @, :, /,
? or # inside it breaks the URL. The resulting failure looks exactly like a
wrong password rather than a quoting problem, which is a bad hour to spend.
Then bring it up:
docker compose -f docker-compose.selfhost.yml --env-file .env.selfhost pull
docker compose -f docker-compose.selfhost.yml --env-file .env.selfhost up -dRun `pull` first — `up -d` alone will BUILD
Compose treats a service that has a build: section as buildable, so a missing
image is compiled from source instead of downloaded. Skipping the pull
costs you the twenty minutes the published image exists to save, and it does it
silently. A measured clean-box run is about 88 seconds with pull, against
roughly six minutes building on a 16-core machine.
Images are published at ghcr.io/gjovanov/roomler-ai for linux/amd64 and
are anonymously pullable. On arm64 — Apple Silicon, a Raspberry Pi, an ARM VPS —
there is no published image yet, so add --build and compile from source.
Watch it come up:
docker compose -f docker-compose.selfhost.yml --env-file .env.selfhost logs -f roomlerAdd your first machine
Your server serves its own installers, and the script it hands out names
your origin rather than roomler.ai — so a machine enrolled from a
self-hosted instance enrols against that instance. Mint a token in your own
dashboard and follow Get started.
Putting it behind a hostname
There is no automatic TLS, deliberately: terminate it in whatever you already run — nginx, Caddy, Traefik, a cloud load balancer. Point the proxy at the container’s HTTP port and set your public origin in the environment so sign-in returns, invite links and the CORS policy all agree about who you are.
Conference media — the one part a proxy cannot fix
This is the failure people hit
Remote desktop, tunnels, SSH and the mesh are peer-to-peer with a relay fallback, and work through anything. Conference video is different: browsers send media straight at an address the server advertises, on a UDP port range, with no reverse proxy involved.
If signalling looks perfect and no video ever arrives, this is nearly always the cause. A successful transport connect proves only that the browser sent its parameters — it says nothing about whether packets can flow.
Two settings decide it:
| Setting | What it must be |
|---|---|
| The announced IP | The address browsers should send media to. 127.0.0.1 works only for calls made on the host itself. |
| The RTC port range | 32 ports are mapped by default — enough to try it. A real deployment wants the full range. |
On Linux, the clean answer is host networking: give the roomler service
network_mode: host and drop its ports: block, and the whole range is
reachable. That is how the hosted service runs. It is not available on Docker
Desktop for macOS or Windows, which is why the port-mapped form is the default.
Optional integrations
All off by default; each is a block of environment variables:
— Invites, notifications and account activation.
OAuth sign-in
— Google, GitHub, Microsoft, LinkedIn and Facebook.
Web push
— Browser notifications when someone is offline.
Billing
— Stripe, if you are reselling access.
Upgrading and backups
Upgrading is pull then up -d. State lives in the MongoDB and MinIO volumes —
back those up, not the container.
Known limitations, stated plainly
- Single node. The compose stack runs one API instance. Multi-node scale-out is supported by the code, but it is a Kubernetes topology rather than this file.
- No automatic TLS. Deliberate — see above.
- arm64 builds from source. No published arm64 image yet.
- The relay is optional. A separate TURN server is not on the critical path: the built-in relay floor over the API’s own port already guarantees connectivity when no direct path exists.
Licensing
The server is AGPL-3.0; the agent that runs on your machines is MPL-2.0.
Running it for yourself, your family or your company is exactly what the licence
is for. See LICENSING.md
for the split and what it means if you intend to offer it as a service.