Cluster
Run one Minecraft network across several machines, joined with a single code.
One node is the leader and decides, the others are workers and execute.
For a step-by-step guide to adding a machine, see Adding a node.
Setting it up
The leader has to be reachable for the other machines. In its mutecloud.toml:
[api]
bind = "0.0.0.0:8770"
token = "..."Then one command on the leader prints everything a new machine needs:
node join
node join 100.100.20.100Without an address it picks its best own one, first a Tailscale address, then one from the
local network. The output is a join code of the form 10.0.0.1:8770#<key> and a ready
one-liner each for Linux and Windows. On the new machine that one-liner is all it takes: the
installer downloads MuteCloud, the setup recognises the code, checks the connection to the
leader, suggests its own address, refuses node names that are already taken, offers the groups
that are pinned to specific nodes and writes the configuration as a worker. If the installer
is already running, pick "Add this machine to an existing network" in the setup and paste
the code.
The code contains a join key, not the cloud's token. It can only add nodes, fetch the
templates of their groups and enter the new node into game groups. On first connect the
leader hands every node its own token, which the node uses from then on. node join new
replaces the key without disconnecting anyone, node remove <name> locks a single node out and
invalidates its token. The code still belongs only on machines you own, and node join
requires full access.
Done by hand, the same looks like this on the worker:
host = "10.0.0.2"
[cluster]
role = "worker"
leader = "10.0.0.1:8770"
token = "..."
advertise = "10.0.0.2"advertise is the address under which the game servers of this node are reachable. The
servers listen on it, and the proxy connects to that address.
Private network only
For security reasons, nodes cannot be joined over the open internet. A cluster needs an interface through which a node accepts commands, hands out templates including every plugin configuration and password, and starts game servers. Exposed to the internet, that interface would be an attack surface where a single intercepted or guessed token opens the entire network. On top of that, the proxy talks directly to the game servers on the other nodes, and those servers must not be reachable from the internet either.
MuteCloud enforces this on both sides. A worker only connects to a leader under a private
address, and the setup already refuses other codes when they are entered. The leader only
accepts nodes and template downloads from private networks and also refuses requests that
arrive through a reverse proxy. Private means 10.0.0.0/8, 172.16.0.0/12,
192.168.0.0/16, 100.64.0.0/10, and for IPv6 fc00::/7 and fe80::/10.
There are many ways to build that shared network:
| Option | When it fits |
|---|---|
| Tailscale | quickest to set up, works through NAT and home connections |
| Headscale | Tailscale with your own coordination server, no third-party accounts |
| NetBird | open alternative to Tailscale, can be self-hosted |
| ZeroTier | virtual LAN, also without port forwarding |
| WireGuard | configured by hand, lean and without extra services, needs one open UDP port |
| Your host's private network | servers at the same provider, for example a cloud network or VLAN |
| LAN | all machines are in the same local network |
The leader's control port 8770 is only opened towards the private network, never towards the internet.
Where things live
The leader owns groups, ranks, roles, tokens, network texts and the shared storage. A worker forwards any request concerning them to the leader, so administration is possible from any node.
Templates are pulled by each worker. It compares its revision with the leader every thirty seconds, downloads only what has changed, unpacks the archive next to the existing copy and swaps the directories afterwards. If a transfer fails, the previous template remains in place.
Placement
Groups run on every node unless told otherwise:
nodes = ["node-1"]The same at runtime, from any node and without a restart:
groups BedWars set nodes node-1,node-2 exactly these nodes
groups BedWars set nodes +node-3 add one node
groups BedWars set nodes -node-3 remove one node
groups BedWars set nodes all run everywhere againPlacement of a new server follows the available memory. If no node has enough, the log reports it at most once every five minutes.
node state of this node
node list all nodes with memory and server count
node reload re-read the configuration
node join new new join key, older codes stop working
node remove <name> lock a node out and invalidate its token
node restore <name> lift the lock, the node needs a new codeWhen a node loses its connection, the webhook reports it and the players of that proxy are removed from the player list.