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:

AppWhat it needs
Game serversa UDP or TCP port
VoIP / PBX (Asterisk, FreeSWITCH)a UDP port for SIP, a UDP port range for RTP, realtime mode
GPS trackers, IoT devicesa TCP or UDP port
MQTT brokers, RADIUSa TCP or UDP port
Chat, notifications, live dashboardsnothing — 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:

  1. Name — a lower-case name such as game, sip or rtp.
  2. Protocol — TCP or UDP.
  3. How many — one port, or a range (up to 1,000).
  4. 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:

VariableWhat
NODE_PUBLIC_IPthe server's public IP — where clients connect
PUBLIC_PORT_<NAME>the outside port, such as PUBLIC_PORT_GAME
PUBLIC_PORT_<NAME>_START, _ENDthe first and last port of a range
  • A single port: the outside port (PUBLIC_PORT_GAME, say 10001) 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 _START to _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.

worker.js — a tiny UDP game server
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:

FieldWhat it doesExample (Asterisk)
Before draining, runa 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 0asterisk -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 as asterisk -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:

text
http://payments:8080
  • On any port, TCP or UDP.
  • Each app gets SERVICE_<NAME>_HOST, and for a web app SERVICE_<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).

Stuck?

Talk to our engineers directly — by phone or email.

01782618248shohag.meghly@gmail.com