Networking & Requirements

Everything needed to connect a node to a controller across machines — on a LAN or over Tailscale — and how to debug it when it doesn't work.

What you need

The golden rule: bind 0.0.0.0, not 127.0.0.1

A service bound to 127.0.0.1 (loopback) will never accept connections from another machine — the operating-system kernel drops traffic sent to your LAN or Tailscale IP, even from the same computer. To be reachable, a service must bind 0.0.0.0 (all interfaces).

Banyan handles this for you: both the controller and node bind 0.0.0.0 by default and gate access in the application (password + the Remote-access toggle). So you do not need to change any bind setting — just use the Remote-access toggle in the console.

Verify what a service is bound to

On the machine running the service:

netstat -an | grep 8800        # controller (macOS/Linux/Windows)
# 0.0.0.0.8800 or *.8800  = good, reachable from other machines
# 127.0.0.1.8800          = local only, nobody else can connect

Connecting a node to a controller

  1. Start the controller. Note its address: on a LAN that's http://<controller-LAN-IP>:8800; over Tailscale it's http://<controller-100.x-IP>:8800.
  2. On the node machine, point it at that address: set BANYAN_CONTROLLER=http://<controller-ip>:8800 before starting, or use the console.
  3. Start the node. Watch the startup banner — it shows the controller URL and whether the connection succeeded.
  4. Use the node's Connectivity → RUN CHECK panel to walk each step if it doesn't connect.

Tailscale setup (cross-network)

  1. Install Tailscale on both machines and sign both into the same tailnet.
  2. Run tailscale status on each — each should list the other machine.
  3. Get the controller's Tailscale IP: tailscale ip -4 (a 100.x.y.z address).
  4. Point the node at http://100.x.y.z:8800.
  5. Test from the node machine: curl http://100.x.y.z:8800/v1/network/stats

Troubleshooting connectivity

SymptomLikely cause & fix
Node can't reach controllerController not reachable: confirm it shows 0.0.0.0.8800 in netstat; check the node points at the right IP; check firewall allows 8800.
Works on same machine, not across machinesThe classic loopback trap or a firewall. Confirm 0.0.0.0 bind; allow the port in the OS firewall.
Tailscale IP won't openRun tailscale status on both; confirm same tailnet; use the 100.x IP not the LAN IP; allow the port; check tailnet ACLs.
Remote console URL won't openTurn on Remote access in the console; if still blocked, it's the OS firewall — allow the port (macOS: System Settings > Network > Firewall).
Port already in useThe start script asks whether to use the next free port. Say yes, or stop the other process. Two controllers/nodes shouldn't share a port.
Heartbeats lagging / flappingFlaky link. The node self-heals with a confirmation window (CONFIRMING → IN_SYNC after several good beats) and keeps retrying.

Firewalls

Self-healing & heartbeats

Nodes send heartbeats to the controller on an interval. The controller marks a node offline after missed heartbeats and releases its jobs. On reconnect, the node confirms stability over several consecutive heartbeats (the CONFIRMING state) before being treated as fully in-sync — this avoids flapping on a single lucky beat. All of this is visible in the node's Connectivity panel and the controller's fleet view.