📘 Reflector Setup Guide

You are in the right place if you want to add your own reflector to the DVPX network. This page walks through the whole path: what you need, which steps are yours, and which belong to the dashboard administrator.

Ready to start?
Sign up as a reflector operator and file your request from your panel.
🛰️ What is a reflector?

A reflector is the server where DVPX users meet. Apps connect to it over TCP, announce their identity and pick a talkgroup; while someone talks, voice packets travel to the reflector over UDP and it forwards them to everyone on that talkgroup.

Reflectors also link to each other: a talkgroup is shared across the whole network, so which reflector you are on never splits a conversation. A reflector NEVER connects to the database — it talks to the dashboard only through the API token issued to it. That is why no database credentials ever live on your server.

🧭 The process, end to end
  1. Sign up. Your callsign becomes your username. Your account waits for administrator approval.
  2. File a reflector request. Give your server address, ports and justification. You do not have to rent the server before this step, but you do need to know its address.
    The form also asks for SSH details, but they are OPTIONAL — you may leave them empty. If you provide them, an administrator can look at your server during an outage and help with the setup; the password is stored encrypted and only an administrator can read it.
  3. Approval and token. The administrator approves the request and creates the reflector record. The moment they generate a token it waits for you HERE ON THIS PANEL: press ⚿ Show Token in the My Reflectors list on "My Panel" and it appears together with a ready-made config.json. The token does NOT need to be sent over WhatsApp or e-mail — and it should not be.
    While waiting on the panel the token is stored encrypted, with the key kept outside the database. Once installed, clear it with "Got it, clear from my panel".
  4. Prepare the server. Install Node.js, place the files, write config.json, open the ports, start the service. The sections below cover this.
  5. Verify. Once your reflector starts reporting to the dashboard it shows as LIVE in the "My Reflectors" list on your panel. If it does not, the same row tells you why.
🔢 How are talkgroup numbers chosen?

A talkgroup number BEGINS WITH THE COUNTRY CODE. The country you pick when signing up determines that code, and you may only request numbers starting with your own. This splits the number space by country so operators from two countries never ask for the same number.

Number Meaning Who creates it?
90 Türkiye, country-wide Administrator
9034 Türkiye / Istanbul (34 = plate code) You request it
9006 Türkiye / Ankara You request it
1 United States, country-wide Administrator
You do NOT type the country code in the form. In the request dialog your country code is shown fixed in front of the field; you type only the region/city code and the number is assembled for you. Example: if your code is 90 and you type 01, the request goes as 9001 — you also see the full number under the field as you type.
The country code ITSELF (e.g. 90) is the country-wide group and is created by an administrator when the reflector is set up; you do not need to request it.
✅ What you need
Minimum Comfortable
CPU1 core4 cores
RAM1 GB4 GB
Disk5 GB30 GB
Operating system Ubuntu 22.04 / Debian 12 Ubuntu 24.04
Public address Static IP (required) Domain name + static IP
Node.js1820
🍓 A Raspberry Pi or mini PC works too. The reflector does not have to run on a VPS. A Raspberry Pi 4 / 5 or a similar mini computer (Intel NUC, thin client, an old laptop…) is fine as long as it meets the hardware bar above. What matters is not the kind of box but the requirements it meets: enough CPU/RAM/disk, the ability to stay up 24/7, and a stable public address.
If you use a Raspberry Pi: install 64-bit Raspberry Pi OS (Debian based) — every command in this guide works unchanged. Prefer booting from an SSD/USB disk rather than an SD card; under constant writes SD cards last months, not years, and they die without warning.
⚠️ A static public IP is MANDATORY. Apps must reach the reflector DIRECTLY from the internet; if the address changes, every user drops. That is why a static public IP is a requirement, not a preference. VPS providers give one by default; a home connection is usually unsuitable (dynamic IP, NAT, closed ports).
🌐 A domain name also works — and is better. You can point a domain name at it instead of an IP (e.g. dvpx.yoursite.com). It is better because if you ever move the server to another provider you change only the DNS record — users never touch their settings. If you must try it on a home connection with a dynamic IP, use a DDNS name — but that is a workaround, not a substitute for a static IP.
🔌

Open ports

TCP 62070 (signalling), UDP 62071 (audio). The UDP port must be open both inbound and outbound — the reflector-to-reflector link uses it too.

⚿

API token

The administrator generates it; it lands encrypted on your panel. Without it the reflector gets no policy and is never published to the apps.

🌐

Stable address

A static public IP is required; pointing a domain at it is better. Users keep their settings even if the server changes.

🔐

SSH access

The IP, username (usually root) and password or SSH key from your provider. You will do the install over that connection.

⌨️ Installation — copy-paste commands
The commands target Ubuntu/Debian and assume you are root. Raspberry Pi OS is Debian based, so the same commands work there unchanged (the Node.js setup script detects arm64 by itself). The detailed walk-through (expected output, troubleshooting, provider firewalls) lives in KURULUM.md inside the reflector package.

1) Node.js

Check first: if the version is 18 or newer, skip this step.

node --version
curl -fsSL https://deb.nodesource.com/setup_20.x | bash - apt install -y nodejs

💡 You do NOT need to run npm install. The DVPX reflector has no external dependencies; it uses Node's built-in modules only.

2) Get the files from the repository

The files live in a public GitHub repository. Using Git is recommended: updating later becomes a single command.

sudo apt install -y git sudo git clone https://github.com/cektor/DVPX.git /opt/dvpx-reflector cd /opt/dvpx-reflector node --check src/index.js # syntax check

Do not leave out the /opt/dvpx-reflector path at the end of the command: giving the target directory explicitly puts the files exactly there (no mkdir needed, git creates the directory itself). We pick that path on purpose — the dvpx-reflector.service file shipped in the repository uses the same path, so you can copy it without editing. The repository is the reflector only; the dashboard runs at the centre of the network and is never installed on your server.

3) config.json

Save the template below as /opt/dvpx-reflector/config.json. The dashboard URL is pre-filled for this installation; replace the token with the value the administrator gave you. The ports must MATCH what you wrote in your request.

{ "serverName": "DVPX-YOUR-NAME", "bindAddress": "0.0.0.0", "tcpPort": 62070, "udpPort": 62071, "dashboard": { "url": "https://dvpx.algsoft.net.tr/reflector.php", "token": "PASTE_THE_TOKEN_FROM_YOUR_ADMIN" }, "peers": { "enabled": true, "bridgeTalkgroups": "all" }, "logLevel": "info", "logCalls": true }
🔒 The token is a password. Never share it, never show it in a screenshot, never paste it on a forum. Make the file root-readable only:
chmod 600 /opt/dvpx-reflector/config.json

4) Firewall

⚠️ Allow 22/tcp BEFORE running ufw --force enable; otherwise you cut your own SSH connection and lock yourself out.
ufw allow 22/tcp ufw allow 62070/tcp ufw allow 62071/udp ufw --force enable ufw status

Your provider may run a SECOND firewall in front of the server (Hetzner, AWS, Oracle Cloud, Azure, Google Cloud…). This is the most commonly missed step: add the same rules there too.

5) Trial run

cd /opt/dvpx-reflector node src/index.js

If the log looks clean (registered with the dashboard, TCP/UDP listening), stop it with Ctrl+C and move on to the service.

6) Install as a service

This keeps it running after you close the SSH window, starts it on boot and restarts it after a crash. Do NOT run the reflector as root.

useradd --system --no-create-home --shell /usr/sbin/nologin dvpx chown -R dvpx:dvpx /opt/dvpx-reflector chmod 600 /opt/dvpx-reflector/config.json cp /opt/dvpx-reflector/dvpx-reflector.service /etc/systemd/system/ systemctl daemon-reload systemctl enable dvpx-reflector systemctl start dvpx-reflector systemctl status dvpx-reflector

If your install path is not /opt/dvpx-reflector, fix the WorkingDirectory, Documentation and ReadWritePaths lines in the service file. If node lives elsewhere (which node), fix ExecStart too.

🔎 Verify that it works

Is the log clean?

journalctl -u dvpx-reflector -f journalctl -u dvpx-reflector -p err -n 50

Are the ports listening?

ss -lntup | grep -E '62070|62071'

Does the dashboard see it?

Look at "My Reflectors" on your panel. Four conditions must hold TOGETHER for the reflector to count as LIVE:

  • An API token must have been issued
  • The record must be approved by an administrator
  • Status must be "online" (not maintenance)
  • It must have reported to the dashboard within 2 minutes

If any one is missing, the row says NOT LIVE and names the condition that is missing.

⬆️ Updating
If an administrator left a red “update order” warning on your panel, follow the steps below. It takes a few minutes and your reflector drops off the network briefly while you do it.

1) Back up first

config.json is your own file and is not kept in the repository, so git pull never overwrites it. Still, a backup costs a minute and saves you from having to ask for a new token.

cp /opt/dvpx-reflector/config.json ~/config.json.yedek

2) Download and install the update

If you installed with Git (the recommended path) it is five commands in total. The first line installs git if it is missing.

sudo apt install -y git sudo systemctl stop dvpx-reflector cd /opt/dvpx-reflector sudo git pull sudo chown -R dvpx:dvpx /opt/dvpx-reflector sudo systemctl start dvpx-reflector
⚠️ Run git pull inside /opt/dvpx-reflector. In any other directory you get a "not a git repository" error.

If you did not install with Git (one-off migration)

If the directory is not a git repository (you installed from a zip), re-clone it once; your config.json is preserved. Every later update becomes the five commands above.

sudo apt install -y git sudo systemctl stop dvpx-reflector sudo mv /opt/dvpx-reflector /opt/dvpx-reflector.eski sudo git clone https://github.com/cektor/DVPX.git /opt/dvpx-reflector sudo cp /opt/dvpx-reflector.eski/config.json /opt/dvpx-reflector/ sudo chown -R dvpx:dvpx /opt/dvpx-reflector sudo chmod 600 /opt/dvpx-reflector/config.json sudo systemctl start dvpx-reflector # once everything works, delete the old folder: sudo rm -rf /opt/dvpx-reflector.eski

3) How do you know it worked?

You will see the new version number in the log. The moment the reflector checks in with the dashboard (a few minutes at most) the red warning on your panel clears ITSELF — there is no “I read it” button and you need not notify anyone. If the warning stays, the update did not actually take effect.

systemctl status dvpx-reflector node /opt/dvpx-reflector/src/index.js --version
🛠️ The three most common problems
Symptom Cause Fix
Service runs, log is clean, nobody can connect Ports closed — usually at the provider firewall Open 62070/tcp and 62071/udp in ufw AND the provider panel
Signalling works but no audio The UDP port is closed (TCP opened, UDP forgotten) Open UDP 62071; a TCP rule does not cover UDP
Dashboard says "never connected" The url or token in config.json is wrong Check the URL and token, then: systemctl restart dvpx-reflector

After every config.json change you must RESTART the service; the file is read only at startup.

🤝 What is expected of an operator
  • Keep your reflector up. Announce planned maintenance in advance.
  • Keep the software current; move to a new version when the administrator announces one.
  • Keep the API token secret. If you suspect it leaked, use "Request New Token" on your panel IMMEDIATELY.
  • When something goes wrong, write from 💬 Chat on your panel; the conversation updates live and every dashboard administrator sees it.
  • Do not run the reflector as root; use an unprivileged user.
  • Keep your instant-messaging details current — that is how you are reached in an emergency.
  • Do not record user traffic or pass it to third parties.
An administrator can un-approve a reflector or revoke its token; the reflector then drops off the network immediately. You are always told why.
🔐 Your permissions in this panel

A reflector-operator account is DELIBERATELY very limited. Nothing you do touches the network directly; everything passes through administrator approval.

You can

  • File a reflector request
  • File a talkgroup request
  • Withdraw your own pending request
  • Watch your own reflector status
  • See your own talkgroups and the stations connected to them
  • Watch last heard for your own reflectors and talkgroups
  • Read the API token issued to you from your panel
  • Request a new token, with a justification
  • Chat with an administrator from your panel
  • Change your own contact details and password

You cannot

  • Create, disable or delete talkgroups
  • Approve reflectors or generate API tokens
  • See the user list or block users
  • View last heard or statistics for the WHOLE network (you only see your own scope)
  • View person-to-person private calls or private talkgroup traffic
  • See another operator's request or details