# Multi-Server

Source: https://docs.gryt.chat/docs/server/multi-server

Connecting to multiple Gryt servers

Gryt clients can connect to multiple servers at the same time. Each server is an independent instance with its own channels, members, and configuration. The client manages connections separately, keyed by the server's host address.

## How it works

When you add a server, the client opens a Socket.IO connection to that server's address. You can add as many servers as you like — each one gets its own entry in the sidebar, and you switch between them by clicking. Only one server is "active" (visible) at a time, but connections to other servers stay open in the background so you receive notifications and presence updates.

There is **no state sync** between servers. Each server is fully independent — your channels, roles, and voice sessions are per-server.

## Adding a server

### Manual

1. Click the **+** button in the server sidebar
2. Enter the server address (e.g. `my-server.example.com:5000`)
3. Click **Connect** — the client fetches the server info via HTTP
4. Click **Join** to complete the handshake

### LAN discovery (desktop app only)

The desktop app automatically discovers Gryt servers on your local network using **mDNS** (multicast DNS). Servers advertise themselves as `_gryt._tcp` services, and the client listens for these announcements.

When a server is found on the LAN, it appears in the **Local Servers** section of the Add Server dialog. Click **Connect** to fetch its info and join — no need to type an IP address.

<Callout type="info">
LAN discovery only works in the desktop (Electron) app. Browsers cannot perform mDNS queries, so the web app always requires manual entry.
</Callout>

#### How mDNS works

- The server publishes a `_gryt._tcp` mDNS service on startup
- On Linux with avahi-daemon (the default on Debian and Ubuntu), it writes a service file into `/etc/avahi/services/` — the most reliable method
- If avahi is not available, it falls back to the `bonjour-service` npm package
- The Electron client browses for `_gryt._tcp` services and shows what it finds
- On shutdown the server withdraws the advertisement, waiting for the goodbye packets before the process exits so the record does not outlive it

Each server writes its own file, named `gryt-<instanceId>-<port>.service`. Two
servers on one host share that directory, which is exactly what `network_mode: host`
with the bundled compose file gives you, and both used to write `gryt.service`. The
second to start silently overwrote the first, so only one was discoverable, nothing
errored, and both worked fine by address. It looked like flaky mDNS rather than one
server replacing another's advertisement. The port is part of the name as well as
the instance ID, because `SERVER_INSTANCE_ID` defaults to `default` when unset and
two servers on one host cannot share a port.

#### Server configuration

mDNS advertisement uses the existing `SERVER_NAME` environment variable. The name shown during discovery is whatever you set as `SERVER_NAME` in your server config.

For Docker deployments with avahi (recommended):

1. Ensure `avahi-daemon` is running on the host
2. Run `chmod o+w /etc/avahi/services` on the host (one-time)
3. The compose file mounts `/etc/avahi/services` into the container automatically

### LAN open join (skip invite codes on LAN)

By default, every new user must use an invite code to join a server. For LAN parties and local events, you can enable **LAN open join** so anyone on the same local network can join without an invite code. Remote connections from the internet still require an invite.

To enable it, go to **Server Settings → Overview** and toggle **Allow anyone on LAN to join**. This setting is dynamic — it takes effect immediately with no server restart.

When enabled, the server checks the connecting client's address. The ranges it treats as local are `10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`, `127.0.0.0/8` and `169.254.0.0/16`, plus IPv6 link-local and unique-local. Anything else still needs an invite.

<Callout type="info">
LAN open join works with both manual connections and mDNS discovery. When a LAN-discovered server has this setting enabled, the desktop app skips the invite code prompt entirely — users just click **Connect** and **Join**.
</Callout>

<Callout type="warn" title="Behind a proxy this needs configuring">
Behind a reverse proxy or a tunnel, LAN open join does nothing until you set
`GRYT_TRUSTED_PROXY_HOPS`.

The server ignores `X-Forwarded-For` unless you tell it how many proxies to
believe, and it defaults to zero, because that header is something any client
can set. Left at zero behind a proxy, every request arrives wearing the proxy's
address rather than the client's, and the server has no way to know who is
actually on the local network.

Rather than treat the proxy's address as the client's, which would let anybody
who can reach the proxy join without an invite, the server keeps the invite
requirement and says so in its log once. Set the hop count to the number of
proxies in front of it and the feature works as intended. See
[Behind a proxy](https://docs.gryt.chat/docs/guide/configuration#behind-a-proxy).
</Callout>

## Removing a server

Right-click a server in the sidebar and select **Remove**, or go to **Settings → Servers** and remove it from there. This closes the connection and removes it from your saved server list.
