A node is the data plane: a stateless agent that terminates client traffic and does what the panel tells it. It has no database, no admin interface and no settings file worth backing up — its only state is a pair of certificates. That is deliberate: a lost node is re-registered in the panel and comes back identical, and nothing on it needs to be migrated or restored.
You almost never install a node from this page. The panel installs nodes for you, and the one-line command it gives you is the supported path. This page covers that path in detail, and then the manual one for the cases the panel cannot reach.
- A Linux server with systemd — or with Docker, which the installer will use instead. amd64, arm64, armv5/v6/v7, 386, s390x and riscv64 are all published.
- Root, or a user with sudo.
- TCP 62050 reachable from the panel, over IPv4 or IPv6 — a node listens on both. Outbound access to the panel is needed by the one-line command below, but not by the automatic install, which pushes everything down its own SSH connection.
- Whatever ports the inbounds you assign to this node will listen on.
A node does not need a licence key of its own. The panel's licence caps how many nodes it will drive.
In the panel, add the node and copy the command it shows:
curl -fsSL https://PANEL/install-node.sh | bash -s -- --panel PANEL --token TOKENRun it as root on the new server. In under a minute it:
- Detects the architecture and downloads the node binary from your panel, not from the internet.
- Exchanges
TOKENfor the panel's mTLS client certificate. The token is single-use and expires; the panel consumes it on this request and it never works again. - Generates the node's own server certificate and key.
- Writes
/opt/nexora-node/config.jsonand a systemd unit, then starts the service.
The panel connects, records the node's certificate fingerprint on that first connection, and from then on accepts only that certificate — trust on first use. The node in turn accepts only the panel's client certificate. Nothing else on the internet can talk to port 62050 in a way that gets past the TLS handshake.
Options you can add to the command:
| Flag | Effect |
|---|---|
--listen ADDR |
bind the control API somewhere other than [::]:62050 (the IPv6 wildcard, which serves IPv4 too) |
--method script|docker |
deploy as a systemd service or a container (default: whatever is already there) |
--source panel|github |
take the binary from the panel or from a public release |
--version TAG |
install a specific release (implies --source github) |
--image REF |
the container image for --method docker |
--binary-url URL |
take the binary from somewhere other than the panel |
--binary-file PATH |
use a binary already on this server |
--ca-file PATH |
use a panel certificate already on this server, instead of a token |
--insecure |
do not verify the panel's TLS certificate while downloading |
--detect |
print what is on this server and exit, changing nothing |
--uninstall |
stop and remove the node |
If the panel has no binary staged for this server's architecture, the install
fails at step 1 with node binary not provisioned. Stage it on the panel
host and re-run — do not fall back to a manual install just for that:
# on the panel server
curl -fsSL -o /tmp/node.tar.gz \
https://github.com/nexora-vpn/node/releases/latest/download/nexora-node-linux-armv7.tar.gz
tar -C /tmp -xzf /tmp/node.tar.gz
install -m 0755 /tmp/nexora-node/nexora-node /var/opt/nexora/bin/nexora-node-linux-armv7The panel can do all of the above for you over SSH. In the node's install drawer — or at the bottom of the add-node dialog — switch to Automatic (SSH), give the panel a login for the server, and press Detect.
It reports what the server is (distribution, architecture, whether it has systemd or Docker) and what is already on it, then shows the plan: which deployment it will use and which version it will land on. Press Install and the output streams back live.
It differs from the command above in one way that matters: it uploads. The installer, the panel's client certificate and — by default — the node binary all go down the SSH connection. The server needs no route to the panel, no route to GitHub and no trust in the panel's TLS certificate. Nothing is fetched, so no install token is involved either.
Run it again later and it updates in place, keeping config.json and the node's
own certificate, so the panel's pin still matches and nothing is re-registered.
Nothing is stored by default. The password is used for that one connection and dropped.
Tick Authorise the panel's key on this server and the panel appends its own
public key to the login user's authorized_keys, then proves it works by opening
a second connection with it. After that, updates need no password at all. It is a
persistent grant of whatever that user can do — revoke it from the node's menu
(Revoke panel key), or by deleting the nexora-panel-… line from
~/.ssh/authorized_keys yourself.
The login user does not have to be root. A user with sudo works; the panel escalates with the password you gave it.
The first connection pins the server's SSH host key, the same way the panel pins a node's TLS certificate. If the server later presents a different key the panel refuses to connect and says so, rather than carrying on — either the machine was replaced, or something is between you and it.
Use Move to another server in the node's menu rather than editing its address. Three things are pinned to the machine a node used to run on: the TLS certificate pin, the SSH host key, and what the panel remembers about how the node was deployed. Carried over to a new server, the first refuses every connection, the second refuses every login, and the third has the installer planning a Docker update for a host that may have no Docker. Moving clears all three, then you install on the new server as if it were new — because it is.
Both are picked for you and both can be overridden.
The deployment defaults to whatever the server already runs, so an update never silently changes a node from a container to a service or back. On a fresh server it takes systemd if there is systemd, Docker otherwise. Choosing the other one explicitly is supported: the installer removes the old deployment first, so the two never fight over port 62050.
The version defaults to the node binary staged on your panel — the one the panel installer downloaded alongside itself. Ask for a specific release instead and the server downloads it from GitHub, which is also what happens automatically for an architecture your panel has nothing staged for (it stages amd64 and arm64; armv7, 386, s390x and riscv64 come from the release). A Docker install pulls the matching image tag.
Port 62050 speaks mTLS and rejects everyone but the panel, but there is no reason to advertise it:
ufw allow from PANEL_IP to any port 62050 proto tcpIf the panel reaches this node over IPv6, the rule has to name the address it actually arrives from — a v4 rule does not cover a v6 connection:
ufw allow from 2001:db8::1 to any port 62050 proto tcpClient-facing ports are a separate matter. They are whatever the inbounds assigned to this node use, so they are chosen in the panel — open them here after you have assigned the template, not before.
Nothing special is required, on either side. The node binds [::]:62050 by
default, which on a dual-stack host accepts IPv4 connections as well — so the
same install works whether the panel reaches it over v4, v6, or both. On a host
with IPv6 switched off entirely the node cannot bind that address and falls back
to 0.0.0.0:62050 on its own, which is what that host meant anyway.
A server with only an IPv6 address needs nothing extra either. Add it in the panel with its address written plainly:
2001:db8::1
Brackets are accepted and stripped — [2001:db8::1] and 2001:db8::1 are the
same node. The panel puts them back where the syntax needs them and leaves them
off where it does not: a share link comes out as
vless://…@[2001:db8::1]:443?…, a wireguard profile as
Endpoint = [2001:db8::1]:51820, while a clash or sing-box config and an
OpenVPN remote line carry the bare address. The same goes for Public
address (the address published in links, when it differs from the one the
panel connects to) and for the SSH host of an automatic install.
One thing IPv6 does not change: if an inbound uses TLS with no SNI set, the client validates the certificate against the address, so that address has to be on the certificate. Reissue the node's certificate with the IPv6 address in its SAN list, exactly as you would with an IPv4 one.
Not recommended. It works, and nothing in Nexora forbids it — but read why you probably do not want it before you do it.
The two do not collide. The panel listens on 2095 and the node on 62050; they
install into /opt/nexora-panel and /opt/nexora-node, run as separate systemd
services, and share only the parent of their state directories —
/var/opt/nexora/nexora.db and /var/opt/nexora/bin/ are the panel's,
/var/opt/nexora/certs/ is the node's.
To do it, run the panel's one-line install command on the panel server itself, and bind the control API to loopback, since the panel is already there:
curl -fsSL https://PANEL/install-node.sh | bash -s -- \
--panel PANEL --token TOKEN --listen 127.0.0.1:62050Then add the node in the panel with the address 127.0.0.1. Port 62050 never
leaves the machine, so skip the ufw rule above entirely.
In Docker, the panel repository ships this arrangement as a ready stack:
docker/panel-and-node.
- It publishes the panel's address. A node's IP goes into every subscription link and every client config you hand out. Co-locating means every user — and anyone who collects those configs — learns where your panel lives. If that address is later filtered or blacklisted for carrying proxy traffic, you lose the panel and every subscription URL with it, not just one node.
- One machine, one fate. Client traffic is bursty and unbounded: it eats CPU, memory, file descriptors and bandwidth. A node under load takes the panel down with it — and a panel that is down takes the subscriptions of every other node's users down too.
- It fuses the disposable with the irreplaceable. A node is meant to be thrown away and re-registered; the panel's database is the one thing in the whole fleet worth backing up. On a shared server, reinstalling the node means working on top of that database.
- The uninstall is a footgun.
rm -rf /var/opt/nexorain the uninstall section below deletes the panel's database. On a shared server remove only/opt/nexora-nodeand/var/opt/nexora/certs. - It still costs a node. A co-located node counts against your licence's node cap exactly like any other.
It is a reasonable choice for a lab, a demo, or a small single-server deployment where you accept that the panel and the proxy share one address and one fate. For anything you would be upset to lose, give the panel its own server.
For an air-gapped server, a host without systemd, or a panel you would rather not have reach out from, the same four steps done by hand.
1. Install the binary.
ARCH=amd64 # or arm64, armv7, armv6, armv5, 386, s390x, riscv64
curl -fsSL -o /tmp/node.tar.gz \
https://github.com/nexora-vpn/node/releases/latest/download/nexora-node-linux-${ARCH}.tar.gz
tar -C /tmp -xzf /tmp/node.tar.gz
mkdir -p /opt/nexora-node /var/opt/nexora/certs
install -m 0755 /tmp/nexora-node/nexora-node /opt/nexora-node/nexora-node2. Get the panel's client certificate. Copy it from the node install page in
the panel and save it as /var/opt/nexora/certs/panel_ca.pem. This is a public
certificate, not a secret — a node that has it still cannot do anything to the
panel.
3. Generate the node's own certificate.
/opt/nexora-node/nexora-node gencerts -dir /var/opt/nexora/certs4. Configure and start.
cat > /opt/nexora-node/config.json <<'JSON'
{
"listen": "[::]:62050",
"cert_file": "/var/opt/nexora/certs/ssl_cert.pem",
"key_file": "/var/opt/nexora/certs/ssl_key.pem",
"client_ca_file": "/var/opt/nexora/certs/panel_ca.pem"
}
JSON
cp /tmp/nexora-node/nexora-node.service /etc/systemd/system/
systemctl daemon-reload
systemctl enable --now nexora-nodeThen add the node in the panel with this server's address and port 62050. The panel pins the certificate on its first connection exactly as it would have after the scripted install.
git clone https://github.com/nexora-vpn/node
cd node
mkdir -p certs
cp /path/to/panel_ca.pem certs/
docker compose up -dThe compose file uses host networking on purpose. A node terminates client traffic on whatever ports its inbounds use, and those are chosen in the panel afterwards — with bridged networking every new inbound would mean editing the compose file and recreating the container.
Nodes are updated from the panel: open the node's install drawer, switch to Automatic (SSH), and press Update. The panel uploads the new binary (or pulls the new image), replaces the running one and restarts it. Nothing needs to be typed on the node itself.
The manual command updates in place too — run it again on a server that already
has a node and it detects the existing install, keeps its config.json and
certificates, and swaps only the binary:
curl -fsSL https://PANEL/install-node.sh | bash -s -- --panel PANEL --token TOKENOr replace the binary yourself and restart:
systemctl stop nexora-node
install -m 0755 /tmp/nexora-node/nexora-node /opt/nexora-node/nexora-node
systemctl start nexora-nodeThe certificates are untouched by an update, so the panel's pin still matches and the node comes back without being re-registered.
In Docker: docker compose pull && docker compose up -d.
systemctl disable --now nexora-node
rm -f /etc/systemd/system/nexora-node.service
systemctl daemon-reload
rm -rf /opt/nexora-node /var/opt/nexoraIf the panel is installed on this same server, do not remove
/var/opt/nexora — that is where its database lives. Remove
/var/opt/nexora/certs instead.
Delete the node in the panel too, or it will keep being counted against your licence and keep showing as offline.
| Path | What |
|---|---|
/opt/nexora-node/nexora-node |
the binary |
/opt/nexora-node/config.json |
listen address and certificate paths — nothing else |
/var/opt/nexora/certs/ssl_cert.pem |
the node's own server certificate |
/var/opt/nexora/certs/ssl_key.pem |
its private key |
/var/opt/nexora/certs/panel_ca.pem |
the panel's client certificate, which is the only one accepted |
/etc/systemd/system/nexora-node.service |
the service unit |
Everything else — inbounds, outbounds, endpoints, routing, users — is pushed by the panel and held in memory. There is nothing else on disk to back up.
The install command fails with node binary not provisioned. The panel has
no binary staged for this architecture. Stage it on the panel host as shown
above, then re-run the command with a fresh token.
The install command fails with invalid token or token already used. Node
install tokens are single-use and time-limited. Generate a new one in the panel
and copy the whole command again.
The node runs but the panel shows it offline. Something between the two is dropping the connection. Check, in order:
systemctl status nexora-node # on the node
journalctl -u nexora-node -n 50 # on the node
nc -vz NODE_IP 62050 # from the panel server
nc -vz -6 2001:db8::1 62050 # …if the panel reaches it over IPv6A firewall rule that does not include the panel's address is the usual cause; a
cloud provider security group is the second. If the node is reached over IPv6,
check that the firewall rule is a v6 rule — a v4 one does not cover it — and that
ss -lnt | grep 62050 shows the node on [::] rather than 0.0.0.0, which
means IPv6 is switched off on the host and the node fell back.
The node was reinstalled and the panel refuses it. A reinstall generates a new server certificate, and the panel is still pinning the old one. Delete the node in the panel and add it again — the pin is taken fresh on the next first connection.