
Run your own browser-based conferencing service, verify it locally, and expose its web interface without router port forwarding
MiroTalk SFU is an open-source WebRTC conferencing platform built around a mediasoup Selective Forwarding Unit. This guide walks through the documented Node.js and Docker Compose installation paths, explains the important media-network settings, and shows how to verify the service before making it remotely accessible. Once the local application is working, we use a Localtonet HTTP tunnel to publish its web interface through a public HTTPS address. Because the application separates web traffic from real-time WebRTC media, we also explain exactly what an HTTP tunnel covers and what still requires a deliberate media-network design.
๐ What's in this guide
Understand the MiroTalk SFU network architecture first
MiroTalk SFU provides browser-based rooms for real-time video, audio, screen sharing, chat, file sharing, whiteboarding, recording, and related collaboration features. Its SFU architecture receives media streams from participants and selectively forwards those streams to other participants. That differs from a simple website where every important interaction travels through one HTTP connection.
A deployment has at least two relevant network layers. The first is the web application and signaling interface, which the project exposes on TCP port 3010 in its documented default setup. The second is the WebRTC media path used by mediasoup. The self-hosting documentation identifies TCP and UDP ports 40000 through 40100 as the example media range and uses SFU_ANNOUNCED_IP to tell clients which public address should receive that traffic.
SFU_ANNOUNCED_IP must identify an address or domain that participants can actually reach for ICE and WebRTC connectivity.
A Localtonet HTTP tunnel can publish the MiroTalk web endpoint and give it a public HTTPS address. It does not, by itself, prove that mediasoup's separate TCP and UDP media transports are reachable. A page may load and a room may open while remote audio or video still fails. Do not treat successful page loading as a complete WebRTC connectivity test.
Localtonet supports HTTP, TCP, UDP, and combined UDP/TCP tunnel families, but the supplied project workflow does not establish a complete mapping between MiroTalk's dynamic media range, its announced ICE address, and a Localtonet configuration. We therefore do not prescribe an unverified media-tunneling design. In this guide, Localtonet publishes the HTTPS web interface as a distinct step. For working remote conferences, the media ports and announced address must also be engineered and tested according to the MiroTalk deployment model.
| Traffic | Documented endpoint | Role in this guide |
|---|---|---|
| Application interface | TCP port 3010 | Verified locally, then used as the target of a Localtonet HTTP tunnel. |
| WebRTC media | TCP and UDP 40000 through 40100 in the documented example | Must be reachable separately unless a tested alternative media design is used. |
| Optional WebRTCServer mode | One media port per worker, beginning at 40000 | Can simplify port management, but still requires deliberate public reachability and testing. |
| Public browser origin | HTTPS URL | Localtonet can provide the public HTTPS address for the HTTP application target. |
Prepare the MiroTalk SFU host
Choose a machine that can remain online for the duration of each meeting and has enough CPU, memory, and network capacity for the expected participants. SFU workloads are sensitive to upload bandwidth because the server forwards streams to multiple recipients. Actual capacity depends on resolution, bitrate, participant behavior, enabled features, available bandwidth, and system load, so a generic participant guarantee would be misleading.
The current MiroTalk project README says the application has been tested with Node.js 22.x and gives Ubuntu 24.04 LTS as its package-installation example. The more detailed self-hosting documentation lists Ubuntu 22.04 LTS and Node.js LTS. Because those official pages differ, use Node.js 22.x for the repository workflow covered here and validate the operating-system release against the current project documentation before a production deployment.
Requirements for the Node.js installation
- A Linux host with root or
sudoaccess for system packages. - Git for cloning the repository.
- Node.js 22.x and npm.
- Build tools and Python required by project dependencies.
- FFmpeg if you intend to use functionality that depends on it, including optional RTMP-related workflows.
- A text editor for
.envandapp/src/config.js. - A reachable public IPv4 address or domain for the documented direct WebRTC media model.
Requirements for the Docker installation
- Docker Engine and Docker Compose.
- Git and a local copy of the MiroTalk SFU repository.
- Permission to run Docker and create containers.
- The same media-network planning required by the Node.js deployment.
Requirements for Localtonet integration
- The MiroTalk service must already be running and reachable from the machine on which our client runs.
- The Localtonet client must be installed and running on that device, or on another device that can reach the MiroTalk host.
- A device-specific Localtonet authentication token. Keep it secret and never paste it into public documentation, screenshots, or shared configuration.
- An available relay server selected from the current Localtonet dashboard.
If our client runs on the same machine as MiroTalk SFU, the HTTP target can use the loopback address and port 3010. If it runs on another machine, use the MiroTalk host's reachable private network address instead. The target must be reachable from the client device itself.
Install MiroTalk SFU with Node.js 22.x
The direct Node.js path gives you straightforward access to the repository, environment file, application configuration, and process. The following sequence follows the official quick-start workflow while including the documented Ubuntu package prerequisites.
Install the Ubuntu system packages
The project provides the following Ubuntu package setup. These commands modify the system and require administrative privileges. Review them before running them, especially on an existing server.
export DEBIAN_FRONTEND=noninteractive
apt-get update -y
apt-get install -y \
build-essential \
git \
curl \
wget \
unzip \
tzdata \
software-properties-common \
ffmpeg
add-apt-repository -y ppa:deadsnakes/ppa
apt-get update -y
apt-get install -y python3.8 python3-pip
MiroTalk's documentation directs users to install Node.js and npm with Node Version Manager, but the extracted installation evidence does not provide a specific NVM bootstrap command. Bootstrap commands can change and should not be copied from an unverified location. Install NVM using its current official instructions, then install Node.js 22.x.
Confirm the active runtime before continuing:
node --version
npm --version
The Node.js output should identify a 22.x release for this workflow. If another version appears, switch the active NVM version before installing dependencies.
Clone and initialize the project
Clone the MiroTalk SFU repository
Download the project and enter its working directory.
git clone https://github.com/miroslavpejic85/mirotalksfu.git
cd mirotalksfu
Create the application configuration
Copy the tracked configuration template to the filename used by the running application.
cp app/src/config.template.js app/src/config.js
Create the environment file
Copy the environment template, then edit the resulting .env file for the deployment.
cp .env.template .env
Install the locked dependencies
The detailed self-hosting guide uses npm ci, which installs from the project's lock file.
npm ci
Start MiroTalk SFU
Launch the server from the project directory and keep the terminal open while performing the first verification.
npm start
The repository README also shows npm install in its short quick start, while the detailed self-hosting page uses npm ci. For a checked-out repository with a committed lock file, the documented npm ci path is the more reproducible installation workflow. If it reports a dependency or lock-file error, capture the complete output and compare your checkout and Node.js version with the current project requirements instead of deleting files blindly.
MiroTalk also documents an alternative listen port invocation:
SERVER_LISTEN_PORT=3011 npm start
This guide continues with port 3010 because that is the documented default and the endpoint used in the Localtonet example. If you deliberately select 3011 or another supported value, use that same port during local verification and as the tunnel target.
Install MiroTalk SFU with Docker Compose
Docker packages the application and its runtime into a container, but it does not remove the need to configure the announced WebRTC address or expose the required media traffic. MiroTalk's documented Compose template uses host networking in its example, which lets the container bind through the host network stack.
Install Docker and Docker Compose on Ubuntu
sudo apt install -y docker.io
sudo apt install -y docker-compose
Verify that your operating system's package versions are suitable for the current Compose template. The official project evidence uses the docker-compose command form, so the commands below preserve that documented syntax.
Clone the repository
Obtain the Compose template and application configuration files from the project.
git clone https://github.com/miroslavpejic85/mirotalksfu.git
cd mirotalksfu
Copy the application configuration template
Create the active JavaScript configuration file.
cp app/src/config.template.js app/src/config.js
Copy the environment template
Create .env and edit it before starting the deployment.
cp .env.template .env
Create the active Compose file
Copy the project's Compose template and review docker-compose.yml before using it.
cp docker-compose.template.yml docker-compose.yml
Pull the application image
Retrieve the image referenced by the Compose configuration.
docker-compose pull
Start the deployment
Start in the foreground for the first run so that startup messages remain visible. The project documents adding -d when background operation is desired.
docker-compose up
The documented Compose example mounts .env, app, and public into the container as read-only content and uses network_mode: 'host'. Do not assume that a different network mode or an arbitrary port mapping will preserve mediasoup behavior. If you customize the template, review both the application port and every media-related setting.
Configure the announced address, media ports, and application security

Before treating the installation as remotely usable, edit .env. The documented production example uses the following settings:
ENVIRONMENT=production
SFU_ANNOUNCED_IP=YOUR_PUBLIC_IPV4_OR_DOMAIN
SFU_MIN_PORT=40000
SFU_MAX_PORT=40100
SFU_NUM_WORKERS=4
Replace YOUR_PUBLIC_IPV4_OR_DOMAIN with the public address that remote WebRTC clients can actually use to reach mediasoup. Do not place the Localtonet HTTP hostname there merely because the web page loads through it. An HTTP hostname is not automatically a routable endpoint for the separate dynamic TCP and UDP media transports.
| Setting | Purpose | Deployment consideration |
|---|---|---|
ENVIRONMENT |
Selects production behavior when set to production. |
Use the documented production value for a server deployment. |
SFU_ANNOUNCED_IP |
Advertises the address clients use for ICE and WebRTC connectivity. | It must resolve or route to the actual media endpoint. |
SFU_MIN_PORT |
Defines the start of the mediasoup media range. | Must match the reachable firewall and network configuration. |
SFU_MAX_PORT |
Defines the end of the mediasoup media range. | A wider range permits more dynamic media transports but increases exposed ports. |
SFU_NUM_WORKERS |
Controls the number of parallel media workers. | The documentation says it defaults to the CPU core count when unset. |
SFU_SERVER |
Enables the optional WebRTCServer mode. | Each worker uses one port beginning at 40000, simplifying port management. |
The project's example says a range from 40000 through 40100 supports roughly 50 participants. It also offers a rough estimate of about 100 participants per worker, depending on video quality, bandwidth, and system load. These are planning estimates, not guarantees. Real capacity should be established through representative load tests and monitoring.
Optional WebRTCServer mode
MiroTalk documents an optional mode that can reduce the number of media ports:
SFU_SERVER=true
In that mode, the application creates a WebRTCServer for each worker, with one listening port per worker beginning at port 40000. This may make firewall management simpler. It does not eliminate the requirement for those ports to be reachable, and it does not establish that an HTTP tunnel can transport them.
Firewall implications
For the project's direct public-server model, its example firewall permits TCP 3010 and both TCP and UDP for 40000 through 40100:
ufw status
ufw allow 3010/tcp
ufw allow 40000:40100/tcp
ufw allow 40000:40100/udp
Firewall commands affect the security boundary of the host. Confirm which interfaces and sources should be allowed, preserve administrative access, and follow your organization's network policy. When only the web interface is published through Localtonet, inbound router forwarding for port 3010 is not required. WebRTC media may still need direct inbound reachability under MiroTalk's documented model.
Review application controls before public access
The project includes room passwords, lobby controls, host protection, OIDC authentication, JWT credentials, and spam-mitigation capabilities. Their exact activation details depend on the current MiroTalk configuration files and are outside the extracted setup evidence, so we do not invent values for them here. Before sharing a public URL, inspect app/src/config.js and .env, enable the controls appropriate to your audience, and test both authorized and unauthorized access.
Treat every public meeting link as shareable unless an application control proves otherwise. Use strong credentials, least privilege, restricted room creation, and short-lived sharing practices where available. A tunnel provides connectivity, not application-level participant authorization.
Verify the installation locally before publishing it

Local verification separates application problems from tunnel problems. If MiroTalk is not working on the local machine, creating a public tunnel will not repair it.
Confirm that the process remains running
For Node.js, keep the npm start process visible and check that it does not exit with an error. For Docker, keep the initial docker-compose up session visible while reviewing startup output.
Open the local interface
From the MiroTalk host, open https://localhost:3010, which is the endpoint documented by the repository quick start. The detailed production guide also describes verification through http://YOUR.DOMAIN.NAME:3010, so the scheme presented by a specific configuration may differ.
Handle local certificate behavior carefully
A local development certificate may cause a browser warning. Confirm that the address is your own host before proceeding. Do not train users to ignore certificate warnings on public sites.
Create a test room
Confirm that the landing page loads, room creation follows your configured access policy, and the in-room interface appears.
Test with two independent participants
Use two browsers or devices and verify bidirectional audio and video. A single browser can confirm the interface, but it cannot adequately validate remote media routing.
Record whether the page loads, whether a participant can join, and whether audio and video flow in both directions. These are different outcomes. A Localtonet HTTP tunnel can solve public access to the first layer without automatically solving the third.
Publish the MiroTalk HTTPS interface with Localtonet
After the MiroTalk interface works locally, create an HTTP tunnel with Localtonet. Our client establishes an outbound connection to a Localtonet relay server, so publishing the web endpoint does not require inbound router port forwarding, a public IP address, firewall changes, or VPN setup. The public tunnel remains available only while the selected device is connected and the tunnel is running.
HTTP tunnels support Random Sub Domain, Custom Sub Domain, and Custom Domain process types. Each serves the same target content through a public HTTPS address. Availability can vary, so select only an option currently shown for your account. Exact custom-domain DNS instructions should be taken from the current dashboard and documentation rather than guessed.
Install and run the Localtonet client
Install our client on the MiroTalk host or on a device that can reach it. Keep the client running because the tunnel depends on its outbound relay connection.
Authenticate the client device
Use the device-specific authentication token associated with the client. Never publish, embed, or share this token as part of the MiroTalk configuration.
Select an available relay server
Choose a current server or region from the Localtonet dashboard. Available values can change and should not be hardcoded from an article.
Create the HTTP tunnel
Select the desired HTTP process type and point the tunnel at the MiroTalk service. If both applications run on the same host, use the loopback address with port 3010. If our client runs elsewhere, use the MiroTalk machine's reachable private IP address and port 3010.
Start the tunnel
Creating a tunnel does not make it active. Press Start and confirm that the selected Localtonet device remains connected.
Open the assigned public HTTPS URL
Test the URL from a device outside the host's local network. Confirm that the landing page and room interface load through the public address.
For the current dashboard sequence and field descriptions, consult our Localtonet HTTP tunnel documentation.
The Localtonet URL terminates the public HTTP or HTTPS connection for the configured web target. MiroTalk's SFU_ANNOUNCED_IP controls a different connection path. Using the HTTP hostname for mediasoup without a documented and tested media transport configuration can produce rooms where the interface works but participants cannot exchange media.
What a successful remote test should include
- The public HTTPS page loads without a public certificate warning.
- Expected application authentication or host protection is enforced.
- A permitted user can create or join a room.
- Two participants on separate external networks can send and receive audio.
- Both participants can send and receive video.
- Screen sharing works if it is required for the deployment.
- Stopping the Localtonet tunnel removes public access to the HTTP interface.
Operate and troubleshoot the deployment

Run Node.js under PM2 when appropriate
Running npm start in a terminal is suitable for initial verification, but the process ends when that terminal or session closes. MiroTalk documents PM2 as an optional process manager:
npm install -g pm2
pm2 start app/src/Server.js --name mirotalksfu
pm2 save
pm2 startup
The final pm2 startup command may print an additional system-specific command that must be run to enable startup integration. Read its output carefully. Process supervision keeps the application available, but the Localtonet client must also remain connected for the public HTTP tunnel to remain available.
The local page does not open
- Confirm that
npm startordocker-compose upremains active. - Review startup output for dependency, permission, configuration, or port-binding errors.
- Confirm that both template files were copied to
app/src/config.jsand.env. - Confirm that the browser is using port 3010 unless you intentionally changed
SERVER_LISTEN_PORT. - If port 3010 is already occupied, identify the existing service before changing ports or terminating processes.
The local page works but the Localtonet URL does not
- Verify that the Localtonet client device is connected.
- Confirm that the tunnel has been started, not merely created.
- Check that the HTTP target uses the correct local IP address and port.
- If the client runs in another container or on another machine, remember that its own loopback address does not refer to the MiroTalk host.
- Test the target from the Localtonet client device before changing the public tunnel configuration.
The room loads but participants have no audio or video
This symptom strongly suggests that the HTTP application path works while the WebRTC media path does not. Check the following:
SFU_ANNOUNCED_IPidentifies an address reachable by remote participants.- The configured media range matches the network and firewall rules.
- Both UDP and TCP behavior have been considered for ports 40000 through 40100 in the documented range-based setup.
- If
SFU_SERVER=trueis enabled, the required per-worker ports beginning at 40000 are reachable. - Docker networking has not hidden or remapped the media listeners in a way that conflicts with the announced address.
- The test uses two genuinely separate clients rather than only one local browser.
The application works locally but camera permission is unavailable remotely
Confirm that the remote page is being opened through the assigned Localtonet HTTPS URL rather than a plain HTTP address. Browsers generally require a secure context for camera and microphone access on non-localhost origins. Also check the browser's site permissions and operating-system privacy permissions.
The tunnel disconnects unexpectedly
A Localtonet tunnel is available only while the selected client device is connected and the tunnel is running. Check whether the host slept, restarted, lost network connectivity, stopped our client, or stopped the tunnel. Also verify that MiroTalk itself is still listening on the configured local port.
Configuration changes appear to have no effect
Confirm that you edited the active files rather than the templates. The application uses app/src/config.js and .env after they are copied. A running Node.js process or container may need to be restarted before startup-time settings are reloaded. For Docker, also confirm that the Compose file mounts the files you edited.
Safe update practices
The project documentation includes an updating workflow, but the extracted evidence does not establish its exact current commands. Do not assume that pulling source or replacing an image is sufficient for every release. Before updating, preserve your customized .env, app/src/config.js, and Compose file, read the project's current changelog and update instructions, schedule downtime, and retain a tested rollback copy.
After every update, repeat the local interface check, two-participant media test, authentication test, and public Localtonet URL test. This catches application, dependency, configuration, and WebRTC regressions independently.
Frequently asked questions
Can Localtonet publish the MiroTalk SFU web interface?
Yes. Once MiroTalk is reachable locally on port 3010, a Localtonet HTTP tunnel can publish that target through a public HTTPS address. Our client makes an outbound relay connection, so the web-interface workflow does not require inbound router port forwarding or a public IP address.
Does the HTTP tunnel carry MiroTalk audio and video?
Not automatically. MiroTalk's mediasoup layer uses separate WebRTC media transports, with TCP and UDP ports 40000 through 40100 in the documented range-based example. Publishing port 3010 over HTTP can make the interface available while media remains unreachable. Treat web access and media connectivity as separate tests.
Should I use Node.js or Docker Compose?
Use Node.js when you want direct control over the runtime and process. Use Docker Compose when containerized packaging and restart policies fit your operations. Both methods use the same project configuration concepts, and neither removes the need to configure the announced media address and reachable media ports.
What local URL should I test?
The repository quick start instructs users to open https://localhost:3010. The detailed production documentation also shows http://YOUR.DOMAIN.NAME:3010. The effective scheme can depend on the selected environment and TLS configuration, but port 3010 is the documented default in both cases.
What should SFU_ANNOUNCED_IP contain?
It should contain the public IPv4 address or domain through which remote clients can reach the actual mediasoup media endpoint. Do not assume the Localtonet HTTP hostname is suitable, because an HTTP tunnel and the WebRTC media transports serve different network functions.
Can I reduce the number of WebRTC media ports?
MiroTalk documents the optional SFU_SERVER=true setting. It creates one WebRTCServer per worker, each listening on one port beginning at 40000. This simplifies port management, but those ports and the announced media address must still be reachable and tested.
Does creating a Localtonet tunnel start it automatically?
No. Tunnel creation and tunnel operation are separate lifecycle states. After creating the HTTP tunnel, press Start. The public URL remains available only while the selected client device is connected and the tunnel is running.
Is a public HTTPS URL enough to secure a meeting server?
No. HTTPS protects the browser connection to the public interface, but it does not decide who may create rooms, join meetings, or administer the service. Configure MiroTalk's appropriate host protection, authentication, room controls, and other access restrictions before sharing the URL.
Publish your verified MiroTalk interface with Localtonet
Install MiroTalk SFU, confirm the application and WebRTC media paths independently, then use our HTTP tunnel to give the working web interface a public HTTPS address without inbound router port forwarding.
Get Started Free โ