A self-hosted alternative to Whereby and Zoom, MiroTalk provides browser video rooms, whiteboards, and screen sharing without accounts or time limits.
MiroTalk establishes direct peer-to-peer WebRTC connections between browsers without routing video or audio streams through an intermediate media server. Created by Miroslav Pejic, the application bundles video calls, screen sharing, collaborative whiteboards, and text chat into a single Node.js service. It offers agencies and engineering squads instantaneous meeting rooms free of mandatory software installations, user accounts, or time limits.
Whereby popularized frictionless video calling for client teams: a host sends a room link, and guests join directly in a browser without creating accounts or downloading software. MiroTalk adopts that exact browser-first workflow but eliminates the third-party intermediary. Running on your own domain, MiroTalk gives teams complete control over meeting data, interface branding, and room access. External clients enter a virtual room with a single click, immediately accessing video, text messaging, and shared canvas tools without third-party tracking or cookie banners.
The tradeoff lies in network architecture and hosting overhead. Whereby runs proprietary media servers capable of hosting large groups, billing monthly subscriptions and metering embedded minutes. MiroTalk P2P uses a decentralized WebRTC mesh where participant devices stream directly to each other. This keeps server hosting costs near zero on a basic virtual server, but shifts bandwidth demands to attendee devices. For standups, pair programming, and client calls of two to six people, MiroTalk matches Whereby without recurring fees, while larger groups require dedicated streaming architecture.
Is MiroTalk free for commercial use? Yes. The software is open-source and free to self-host for internal operations and client meetings without license fees.
MiroTalk vs Whereby — which should small teams choose? Whereby fits organizations needing managed cloud infrastructure for large groups without server upkeep. MiroTalk is better for teams wanting self-hosted privacy, custom branding, and zero ongoing subscription costs for small meetings.
Does MiroTalk require a TURN server for self-hosting? Direct connections work across open networks, but production deployments require a TURN relay server so participants behind strict corporate NAT firewalls can connect.
MiroTalk is an effective self-hosted alternative to proprietary meeting tools for small teams that prioritize data privacy and instant browser access over large-group conferencing. Under active maintenance with commits pushed in September 2026, the software is governed by the AGPL-3.0 license, requiring teams that modify and host the code to share their source changes or obtain a commercial exception.
We started this exact setup from scratch in a clean Linux machine on Nov 3, 2026 and checked it through MiroTalk's own API: every check passed. Recipes are re-checked regularly and removed if they stop working.
mkdir -p mirotalk && cd mirotalk
curl -fsSL https://open-source.store/tools/mirotalk/compose.yml -o compose.ymlcat > .env <<EOF
HOST=http://localhost:3000
JWT_KEY=$(openssl rand -hex 32)
API_KEY_SECRET=$(openssl rand -hex 16)
EOFHOST — Public URL of the MiroTalk server used for invite links and room URLs.JWT_KEY — 64-character secret key used to sign and verify JSON Web Tokens. Generated above; keep it secret.API_KEY_SECRET — Secret key used to authenticate requests to the MiroTalk REST API. Generated above; keep it secret.docker compose up -dservices:
mirotalk:
image: mirotalk/p2p@sha256:23036bbd62075385a6f0cbf3815f0dc1530b6c25bc5739474de421bd7ebd9541
user: '1000:1000'
restart: unless-stopped
init: true
stop_grace_period: 15s
ports:
- "3000:3000"
environment:
HOST: ${HOST}
PORT: 3000
JWT_KEY: ${JWT_KEY}
API_KEY_SECRET: ${API_KEY_SECRET}
healthcheck:
test: ["CMD-SHELL", "wget -q -O /dev/null http://127.0.0.1:3000/ || exit 1"]
interval: 5s
timeout: 5s
retries: 5
Free IDE for Java, Kotlin, Go, TypeScript and Python. Built on IntelliJ Community, fully open source, 300+ plugins.
Show that MiroTalk is listed as an open-source alternative. Paste into your README:
Ubuntu 24.04 · Apple container, Docker 29.1.3, Compose 2.40.3+ds1-0ubuntu1~24.04.1. Images pulled in 154 s: mirotalk/p2p@sha256:23036bbd62075385a6f0cbf3815f0dc1530b6c25bc5739474de421bd7ebd9541.
Based on the upstream setup. Official compose template. Image pinned by digest to master commit a6d33e4; secrets and public host moved to .env.
mirotalk-1 | 2026-11-03T03:16:24.931118962Z method: 'GET',
mirotalk-1 | 2026-11-03T03:16:24.931122212Z path: '/node_modules/auth.json',
mirotalk-1 | 2026-11-03T03:16:24.931122795Z body: undefined
mirotalk-1 | 2026-11-03T03:16:24.931123295Z } +1ms
mirotalk-1 | 2026-11-03T03:16:24.931715045Z [11/3/2026, 03:16:24:931] [server] New request: {
mirotalk-1 | 2026-11-03T03:16:24.931717003Z ip: '::ffff:192.168.65.118',
mirotalk-1 | 2026-11-03T03:16:24.931717545Z method: 'GET',
mirotalk-1 | 2026-11-03T03:16:24.931718045Z path: '/node_modules/auth.json',
mirotalk-1 | 2026-11-03T03:16:24.931718753Z body: undefined
mirotalk-1 | 2026-11-03T03:16:24.931719253Z } +0ms
mirotalk-1 | 2026-11-03T03:16:24.931915128Z [11/3/2026, 03:16:24:931] [server] New request: {
mirotalk-1 | 2026-11-03T03:16:24.931916878Z ip: '::ffff:192.168.65.118',
mirotalk-1 | 2026-11-03T03:16:24.931917462Z method: 'GET',
mirotalk-1 | 2026-11-03T03:16:24.931918003Z path: '/storage/auth.json',
mirotalk-1 | 2026-11-03T03:16:24.931918545Z body: undefined
mirotalk-1 | 2026-11-03T03:16:24.931919170Z } +0ms
mirotalk-1 | 2026-11-03T03:16:24.932387712Z [11/3/2026, 03:16:24:932] [server] New request: {
mirotalk-1 | 2026-11-03T03:16:24.932389962Z ip: '::ffff:192.168.65.118',
mirotalk-1 | 2026-11-03T03:16:24.932390587Z method: 'GET',
mirotalk-1 | 2026-11-03T03:16:24.932391378Z path: '/storage/auth.json',
mirotalk-1 | 2026-11-03T03:16:24.932391878Z body: undefined
mirotalk-1 | 2026-11-03T03:16:24.932392503Z } +1ms
mirotalk-1 | 2026-11-03T03:16:24.932809420Z [11/3/2026, 03:16:24:932] [server] New request: {
mirotalk-1 | 2026-11-03T03:16:24.932811003Z ip: '::ffff:192.168.65.118',
mirotalk-1 | 2026-11-03T03:16:24.932811545Z method: 'GET',
mirotalk-1 | 2026-11-03T03:16:24.932812045Z path: '/database/auth.json',
mirotalk-1 | 2026-11-03T03:16:24.932812628Z body: undefined
mirotalk-1 | 2026-11-03T03:16:24.932997878Z } +0ms
mirotalk-1 | 2026-11-03T03:16:24.933110878Z [11/3/2026, 03:16:24:933] [server] New request: {
mirotalk-1 | 2026-11-03T03:16:24.933112878Z ip: '::ffff:192.168.65.118',
mirotalk-1 | 2026-11-03T03:16:24.933113462Z method: 'GET',
mirotalk-1 | 2026-11-03T03:16:24.933114003Z path: '/database/auth.json',
mirotalk-1 | 2026-11-03T03:16:24.933114545Z body: undefined
mirotalk-1 | 2026-11-03T03:16:24.933115045Z } +1ms
mirotalk-1 | 2026-11-03T03:16:24.933549962Z [11/3/2026, 03:16:24:933] [server] New request: {
mirotalk-1 | 2026-11-03T03:16:24.933585670Z ip: '::ffff:192.168.65.118',
mirotalk-1 | 2026-11-03T03:16:24.933586337Z method: 'GET',
mirotalk-1 | 2026-11-03T03:16:24.933586920Z path: '/db/auth.json',
mirotalk-1 | 2026-11-03T03:16:24.933587462Z body: undefined
mirotalk-1 | 2026-11-03T03:16:24.933588087Z } +0ms
mirotalk-1 | 2026-11-03T03:16:24.934031920Z [11/3/2026, 03:16:24:933] [server] New request: {
mirotalk-1 | 2026-11-03T03:16:24.934033920Z ip: '::ffff:192.168.65.118',
mirotalk-1 | 2026-11-03T03:16:24.934034628Z method: 'GET',
mirotalk-1 | 2026-11-03T03:16:24.934035128Z path: '/db/auth.json',
mirotalk-1 | 2026-11-03T03:16:24.934035628Z body: undefined
mirotalk-1 | 2026-11-03T03:16:24.934056962Z } +0ms
mirotalk-1 | 2026-11-03T03:16:24.934174378Z [11/3/2026, 03:16:24:934] [server] New request: {
mirotalk-1 | 2026-11-03T03:16:24.934175878Z ip: '::ffff:192.168.65.118',
mirotalk-1 | 2026-11-03T03:16:24.934176420Z method: 'GET',
mirotalk-1 | 2026-11-03T03:16:24.934176962Z path: '/auth/auth.json',
mirotalk-1 | 2026-11-03T03:16:24.934177587Z body: undefined
mirotalk-1 | 2026-11-03T03:16:24.934178087Z } +1ms
mirotalk-1 | 2026-11-03T03:16:24.934707545Z [11/3/2026, 03:16:24:934] [server] New request: {
mirotalk-1 | 2026-11-03T03:16:24.934709462Z ip: '::ffff:192.168.65.118',
mirotalk-1 | 2026-11-03T03:16:24.934710045Z method: 'GET',
mirotalk-1 | 2026-11-03T03:16:24.934710545Z path: '/auth/auth.json',
mirotalk-1 | 2026-11-03T03:16:24.934711045Z body: undefined
mirotalk-1 | 2026-11-03T03:16:24.934711545Z } +0ms
mirotalk-1 | 2026-11-03T03:16:24.934872670Z [11/3/2026, 03:16:24:934] [server] New request: {
mirotalk-1 | 2026-11-03T03:16:24.934874253Z ip: '::ffff:192.168.65.118',
mirotalk-1 | 2026-11-03T03:16:24.934874837Z method: 'GET',
mirotalk-1 | 2026-11-03T03:16:24.934875378Z path: '/authentication/auth.json',
mirotalk-1 | 2026-11-03T03:16:24.934875878Z body: undefined
mirotalk-1 | 2026-11-03T03:16:24.934876378Z } +0ms
mirotalk-1 | 2026-11-03T03:16:24.935452462Z [11/3/2026, 03:16:24:935] [server] New request: {
mirotalk-1 | 2026-11-03T03:16:24.935456378Z ip: '::ffff:192.168.65.118',
mirotalk-1 | 2026-11-03T03:16:24.935456962Z method: 'GET',
mirotalk-1 | 2026-11-03T03:16:24.935457587Z path: '/authentication/auth.json',
mirotalk-1 | 2026-11-03T03:16:24.935458128Z body: undefined
mirotalk-1 | 2026-11-03T03:16:24.935458628Z } +1ms
mirotalk-1 | 2026-11-03T03:16:24.935585503Z [11/3/2026, 03:16:24:935] [server] New request: {
mirotalk-1 | 2026-11-03T03:16:24.935587128Z ip: '::ffff:192.168.65.118',
mirotalk-1 | 2026-11-03T03:16:24.935587712Z method: 'GET',
mirotalk-1 | 2026-11-03T03:16:24.935588212Z path: '/oauth/auth.json',
mirotalk-1 | 2026-11-03T03:16:24.935588795Z body: undefined
mirotalk-1 | 2026-11-03T03:16:24.935589295Z } +0ms
mirotalk-1 | 2026-11-03T03:16:24.936039670Z [11/3/2026, 03:16:24:935] [server] New request: {
mirotalk-1 | 2026-11-03T03:16:24.936049253Z ip: '::ffff:192.168.65.118',
mirotalk-1 | 2026-11-03T03:16:24.936050045Z method: 'GET',
mirotalk-1 | 2026-11-03T03:16:24.936050628Z path: '/keys/auth.json',
mirotalk-1 | 2026-11-03T03:16:24.936051170Z body: undefined
mirotalk-1 | 2026-11-03T03:16:24.936051712Z } +0ms
mirotalk-1 | 2026-11-03T03:16:24.936330628Z [11/3/2026, 03:16:24:936] [server] New request: {
mirotalk-1 | 2026-11-03T03:16:24.936334128Z ip: '::ffff:192.168.65.118',
mirotalk-1 | 2026-11-03T03:16:24.936334712Z method: 'GET',
mirotalk-1 | 2026-11-03T03:16:24.936335337Z path: '/oauth/auth.json',
mirotalk-1 | 2026-11-03T03:16:24.936335878Z body: undefined
mirotalk-1 | 2026-11-03T03:16:24.936336378Z } +0ms
mirotalk-1 | 2026-11-03T03:16:24.936655295Z [11/3/2026, 03:16:24:936] [server] New request: {
mirotalk-1 | 2026-11-03T03:16:24.936659003Z ip: '::ffff:192.168.65.118',
mirotalk-1 | 2026-11-03T03:16:24.936659628Z method: 'GET',
mirotalk-1 | 2026-11-03T03:16:24.936660128Z path: '/credentials/auth.json',
mirotalk-1 | 2026-11-03T03:16:24.936660670Z body: undefined
mirotalk-1 | 2026-11-03T03:16:24.936661128Z } +0ms
mirotalk-1 | 2026-11-03T03:16:24.936794462Z [11/3/2026, 03:16:24:936] [server] New request: {
mirotalk-1 | 2026-11-03T03:16:24.936795712Z ip: '::ffff:192.168.65.118',
mirotalk-1 | 2026-11-03T03:16:24.936796253Z method: 'GET',
mirotalk-1 | 2026-11-03T03:16:24.936796753Z path: '/keys/auth.json',
mirotalk-1 | 2026-11-03T03:16:24.936797337Z body: undefined
mirotalk-1 | 2026-11-03T03:16:24.936797837Z } +0ms
mirotalk-1 | 2026-11-03T03:16:24.937401670Z [11/3/2026, 03:16:24:937] [server] New request: {
mirotalk-1 | 2026-11-03T03:16:24.937403212Z ip: '::ffff:192.168.65.118',
mirotalk-1 | 2026-11-03T03:16:24.937403837Z method: 'GET',
mirotalk-1 | 2026-11-03T03:16:24.937404420Z path: '/creds/auth.json',
mirotalk-1 | 2026-11-03T03:16:24.937404962Z body: undefined
mirotalk-1 | 2026-11-03T03:16:24.937407545Z } +1ms
mirotalk-1 | 2026-11-03T03:16:24.937691212Z [11/3/2026, 03:16:24:937] [server] New request: {
mirotalk-1 | 2026-11-03T03:16:24.937695212Z ip: '::ffff:192.168.65.118',
mirotalk-1 | 2026-11-03T03:16:24.937696295Z method: 'GET',
mirotalk-1 | 2026-11-03T03:16:24.937751170Z path: '/credentials/auth.json',
mirotalk-1 | 2026-11-03T03:16:24.937755212Z body: undefined
mirotalk-1 | 2026-11-03T03:16:24.937755878Z } +0ms
mirotalk-1 | 2026-11-03T03:16:24.938347003Z [11/3/2026, 03:16:24:938] [server] New request: {
mirotalk-1 | 2026-11-03T03:16:24.938349212Z ip: '::ffff:192.168.65.118',
mirotalk-1 | 2026-11-03T03:16:24.938349878Z method: 'GET',
mirotalk-1 | 2026-11-03T03:16:24.938350503Z path: '/creds/auth.json',
mirotalk-1 | 2026-11-03T03:16:24.938351045Z body: undefined
mirotalk-1 | 2026-11-03T03:16:24.938351545Z } +1ms
mirotalk-1 | 2026-11-03T03:16:29.610623839Z [11/3/2026, 03:16:29:610] [server] New request: { ip: '::ffff:127.0.0.1', method: 'GET', path: '/', body: undefined } +4s
mirotalk-1 | 2026-11-03T03:16:34.677958383Z [11/3/2026, 03:16:34:676] [server] New request: { ip: '::ffff:127.0.0.1', method: 'GET', path: '/', body: undefined } +5s