Running apps
Realtime, TCP/UDP ports and private networks
Game servers, VoIP, IoT or your own TCP/UDP service — how to run apps on Meghly that talk straight on their own ports rather than over HTTP.
Ordinary websites and APIs run over HTTPS, through Meghly's router. Some apps, though, take connections straight on their own ports:
| App | What it needs |
|---|---|
| Game servers | a UDP or TCP port |
| VoIP / PBX (Asterisk, FreeSWITCH) | a UDP port for SIP, a UDP port range for RTP, realtime mode |
| GPS trackers, IoT devices | a TCP or UDP port |
| MQTT brokers, RADIUS | a TCP or UDP port |
| Chat, notifications, live dashboards | nothing — WebSockets work on the ordinary HTTPS address |
Public ports and realtime mode must be part of your hosting plan. How many ports you get depends on the plan — see pricing.
WebSockets
WebSockets (SignalR, socket.io, Laravel Reverb) work on the app's HTTPS address (and on your own domain) with no setting at all. No public port is needed.
Taking a public TCP/UDP port
On the app's General page, under Public TCP/UDP ports:
- Name — a lower-case name such as
game,siporrtp. - Protocol —
TCPorUDP. - How many — one port, or a range (up to 1,000).
- App listens on — for a single port, the port the app listens on inside (such as
7777).
Meghly hands out a free port of the node — TCP from 20000–29999, UDP from 10000–19999. You cannot pick the number yourself, so the app reads it from its variables:
| Variable | What |
|---|---|
NODE_PUBLIC_IP | the server's public IP — where clients connect |
PUBLIC_PORT_<NAME> | the outside port, such as PUBLIC_PORT_GAME |
PUBLIC_PORT_<NAME>_START, _END | the first and last port of a range |
- A single port: the outside port (
PUBLIC_PORT_GAME, say10001) reaches the app's own port inside (7777). - A range: the same numbers inside and out — the app must listen on exactly the ports from
_STARTto_END.
Ports go to the app's worker process. A fixed port cannot be held by two at once, so that worker runs as a single instance.
const dgram = require("node:dgram");
const server = dgram.createSocket("udp4");
server.on("message", (msg, from) => server.send(msg, from.port, from.address));
server.bind(7777); // "App listens on"
console.log(`Players connect to ${process.env.NODE_PUBLIC_IP}:${process.env.PUBLIC_PORT_GAME}`);For safety, some ports are never opened (22, 25, 53, 80, 443, 465, 587, 993 …). For the web, always use the ordinary HTTPS address.
Deploying without cutting live connections (stateful worker)
In an ordinary deploy, the old version stops once the new one is up. In the middle of a game or a call, that cuts everyone off. Under General → Stateful / realtime worker, say how to wait:
| Field | What it does | Example (Asterisk) |
|---|---|---|
| Before draining, run | a command that stops taking new work (optional) | asterisk -rx "core stop gracefully" |
| In progress (prints a number) | prints how much is still running; asked every 5 s, the switch happens at 0 | asterisk -rx "core show channels count" |
| Drain at most (seconds) | the longest wait (10–480, 300 by default) | 300 |
Health checks without HTTP
Apps like these often have no HTTP at all, so there are two other ways to tell that a new version is ready:
- Ready when TCP port accepts — ready once a TCP port inside the app opens (such as
5038). - Ready when this command succeeds — ready once a command exits with
0(such asasterisk -rx "core show uptime").
Realtime mode
For voice, video and games, even a few milliseconds of delay are noticeable. With Realtime mode on, the app runs outside the security sandbox, straight on the server — so latency is low. Every other limit (memory, the read-only file system, its own network) stays as it was, and the app gets at least 1 CPU so audio does not break up.
Private networks (service groups)
When several apps need to talk to each other — say a website, a payment service and a worker — give them all the same Service group name under General → Private network (such as shop). Apps in one group are on the same server, in one private network:
http://payments:8080- On any port, TCP or UDP.
- Each app gets
SERVICE_<NAME>_HOST, and for a web appSERVICE_<NAME>_URL. - With Internal only on, an app has no public address at all — it is reached only from its group (an internal API, for example).