Nodes
A node is any Linux server running xvii-node as an agent. It runs apps' containers with Docker, keeps
their networks and volumes, serves or relays their traffic, and builds images from source. The agent
dials out to the control plane (wss://apps.xvii.network/agent), so a node needs no inbound port for
control and can sit behind NAT (as a worker-only node).
Join a node
In apps → Nodes → Add node, make a one-time join token (valid for an hour), then on the server, as
root:
curl -fsSL https://apps.xvii.network/join.sh | sh -s -- TOKEN relay fra1
The script downloads xvii-node from the release store, checks its checksum, trades the token for the
node's own credential and starts the agent as a service. Arguments: the token, the mode (relay or
edge) and a name. Docker is installed if it's missing (on a busy server, read what it does first).
The node reports its CPU, memory, disk and workloads every 15 seconds; node.online and node.offline
go to notify.
xvii apps nodes
Edge or relay
| Mode |
Traffic |
Needs |
| relay |
Visitors reach the control plane's Caddy, which forwards over HTTPS to the node's agent proxy (port 7443, its certificate pinned) |
port 7443 reachable from the control plane only |
| edge |
The node serves its apps' names itself on 80 and 443 with its own certificates |
ports 80 and 443 free on the node |
A node whose 80 and 443 belong to other software stays a relay. On edge nodes, names attached to their
apps point at the node while it serves; <app>.apps.xvii.network stays relayed either way. Check the node's
reported edge state before pointing DNS at it: switching the mode doesn't prove the ports work.
Relay traffic crosses the control plane, so its bandwidth counts there.
Who may use a node
- A team's node runs that team's apps only.
- An instance node (added by a superuser, or a team's node a superuser promotes) is shared: access for
the owner only, all teams, or listed teams, each with a quota of memory, CPU, disk and workloads.
Memory is never promised twice: the sum of quotas and placed limits stays under the node's allocatable
memory (capacity minus a system reserve). CPU limits are caps, not reserved cores. Leave room for the
system, builds and anything else on the server.
Placement
Apps of one project environment run on one node, so they reach each other and their databases over that
node's team network. A new environment goes to the node with the most free memory the team may use; apps
with volumes or locally built images stay on their node. When the node is full or offline, the deploy says
so.
Isolation
Each team has its own Docker network on a node. Containers publish no host ports, run with
no-new-privileges, a pids limit and dropped capabilities. Containers and networks the agent didn't make
(no xvii.* labels) are left alone, so a node can keep running what it ran before.
Adopt a server's existing projects
A node's existing compose projects can be adopted in place: apps reads the containers, volumes, ports and
the reverse proxy in front, then cuts over per project, starting the new containers on the same
volumes. The cut-over is reversible until confirmed.
Move a project between nodes
xvii apps move fra1 --project shop --env production
See Apps → Move to another node.
Remove a node
Move or delete its environments first. Then remove it in apps → Nodes and, on the server:
xvii-node leave
systemctl disable --now xvii-node
leave forgets the node's credential. Containers it ran are stopped by apps before removal; volumes of
deleted apps are removed by the agent once it reports them gone.
Build caches
Builds leave BuildKit caches on the node. Remove only caches whose owner you know; never run
docker system prune --volumes on a node as routine cleanup: it deletes database volumes too.