Guides · Vaults and teams
Self-hosting the relay
Run the MangoSSH relay on a machine you control. One relay serves every feature that needs a meeting point on the internet: Connect by ID, SSH Persistent Sessions, the PAM Broker and browser access.
- Docker
- One relay for every remote feature
- About 20 minutes
What the relay does
The relay is a single server process. MangoSSH devices, host agents and browsers connect out to it over WebSockets, so nothing you manage needs an open inbound port. Which features use it:
| Feature | What the relay does | Also needs |
|---|---|---|
| Connect by ID | Pairs a viewer with a registered host by Remote ID and password. Carries the session when no direct path forms. | Nothing else |
| SSH Persistent Sessions | Holds the SSH connection open on the relay, so a session survives closing the app or switching devices. Reopen the host to reattach with recent scrollback. | The target must be reachable from the relay. Password sign-in only. |
| PAM Broker | Stores a host's credential and connects on the user's behalf over SSH, RDP or VNC, so the user never sees the password. | A vault server with ticket signing, and the broker settings below |
| Browser access | Serves the in-browser terminal, RDP and VNC pages, and bridges browser share links. | PAM Broker for brokered links, or a host agent for shared desktops |
| RDP recording | Records brokered RDP sessions on its own disk, encrypted, when a host is set to record. | PAM Broker |
SSH Persistent Sessions is turned on per host: open the host with Edit, go to Access, and tick Keep this session alive on MangoSSH's relay — reattach from any device.
The vault server only ever sees ciphertext. The relay is different by design. For Persistent Sessions and the PAM Broker it holds credentials, encrypted at rest with passphrases in its own configuration, and decrypts them in memory to sign in. Run it on a machine you trust as much as the servers behind it. Connect by ID traffic is the exception: the relay forwards it encrypted and cannot read it.
What you need
- A Linux x86-64 machine that runs Docker, reachable from every device and agent that will use it. A small VPS is enough to start.
- Sized for the traffic you send through it. Pairing and forwarding for Connect by ID is light on CPU and limited by bandwidth: every relayed session passes through twice, in and out. Brokered RDP is the heavy case, because the relay runs the RDP client itself and decodes the screen. Add CPU as brokered RDP use grows. Check your provider's outbound traffic pricing before choosing.
- A domain name pointing at the machine, for a Let's Encrypt certificate.
- Ports 80 and 443 open. The relay's own port, 8878, stays private behind the proxy.
Step 1 — Install the relay
Download the relay bundle onto the machine and run its installer. The bundle holds a pre-built image, so nothing is compiled on your server.
Install Docker and the Compose plugin, if the machine doesn't have them.
curl -fsSL https://get.docker.com | shPoint DNS at the machine. Create an A record such as
relay.yourteam.exampleand wait until it resolves. The certificate can only be issued once it does.Download the bundle.
curl -fLO https://downloads.mangossh.com/mangossh/latest/mangossh-relay-linux-x64.tar.gzCheck the download (optional). Print its SHA-256 and compare it with the relay line in SHA256SUMS-selfhost, linked under Self-host the servers in the downloads section. That list names the versioned file, such as
mangossh-relay-1.0.99-linux-x64.tar.gz; it is the same file, so only the hash has to match.sha256sum -cwould look for the versioned name and report it missing.sha256sum mangossh-relay-linux-x64.tar.gzUnpack and install.
tar -xzf mangossh-relay-linux-x64.tar.gz cd mangossh-relay-*-linux-x64 sudo ./install.sh --domain relay.yourteam.exampleCheck it from outside:
curl https://relay.yourteam.example/healthzshould printok.
The installer puts the relay in /opt/mangossh/relay/ and writes its .env with a new random auth token and passphrases. It starts a Caddy proxy that gets and renews the Let's Encrypt certificate, and binds port 8878 to localhost so the relay is only reachable through HTTPS. At the end it prints the Relay URL and Auth token to enter in MangoSSH.
The bundle is built for x86-64 (amd64) servers. The installer pulls the Caddy image from Docker Hub, so the server needs internet access while it runs. In the firewall, open ports 80 and 443 for this service.
Without --domain the relay listens on plain HTTP and WebSocket on port 8878, and the auth token and Connect by ID passwords cross the network in the clear. Use that only on a trusted LAN, or behind your own TLS proxy that passes WebSocket upgrades through.
Step 2 — Point MangoSSH at it
Open Settings → Remote → Relay Server.
Enter the Relay URL, such as
wss://relay.yourteam.example, and paste the auth token the installer printed (RELAY_AUTH_TOKENin/opt/mangossh/relay/.env) into Auth token.Click Save. The row under Saved relay shows the URL and a masked token.
Repeat on every device that uses these features, with the same URL and token. The P2P window's Settings → Relay Server tab edits the same setting.
| URL form | Use |
|---|---|
wss://relay.yourteam.example | The normal choice, behind the HTTPS proxy. |
wss://relay.yourteam.example/v1/session | Also accepted. MangoSSH strips any /v1/… path and adds the right one for each feature. |
ws://192.168.1.20:8878 | Testing on a trusted LAN only. No encryption on the relay connection itself. |
To test the connection, use Test next to Connect by ID — relay in the P2P window's Remote Access settings, or Test Connection in the relay step of the Set up browser access… wizard (right-click a host). Share links use the relay's address, so browsers must be able to reach it too. With a wss:// relay they get an https:// link.
Step 3 — Turn on the PAM Broker (optional)
Skip this unless you use the PAM Broker or brokered browser access. It connects the relay to your vault server, which issues short-lived signed tickets. The relay only checks the signature, so it cannot grant access by itself.
- Vault server on the same machine: nothing to do. When both bundles are installed there, the installer copies the vault server's public key into the relay's
.envand restarts the relay. - Vault server elsewhere: copy the
BROKER_TRUST_PUBKEY=…line from the vault server's/opt/mangossh/vault/.env(the vault installer also prints it) into/opt/mangossh/relay/.env, then runsudo docker compose up -din/opt/mangossh/relay.
The relay's own broker passphrase, RELAY_BROKER_CACHE_PASSPHRASE, was already generated at install. Only the public key is ever copied between machines; the private key stays on the vault server.
Configuration reference
All settings are environment variables, read from /opt/mangossh/relay/.env by Docker Compose. Run sudo docker compose up -d in that folder after changing any of them.
| Variable | What it does | Default |
|---|---|---|
RELAY_AUTH_TOKEN | Shared bearer token every MangoSSH device presents to use Connect by ID, Persistent Sessions and browser share links. Required. | None |
RELAY_CACHE_PASSPHRASE | Encrypts credentials cached for Persistent Sessions. Required. | None |
BROKER_TRUST_PUBKEY | The vault server's public ticket key (64 hex characters). Unset means the PAM Broker is off and all broker requests are refused. | Unset |
RELAY_BROKER_CACHE_PASSPHRASE | Encrypts PAM Broker credentials and RDP recordings. Required when BROKER_TRUST_PUBKEY is set. | Unset |
BROKER_STORE_PATH | File holding provisioned broker credentials. Compose sets it inside the data volume. | /data/broker_store.json |
RDP_RECORDINGS_PATH | Folder for server-side RDP recordings. Compose sets it inside the data volume. | /data/rdp_recordings |
IDLE_TIMEOUT_SECS | How long a Persistent Session is kept with no device attached. | 900 (15 min) |
PUBLIC_PORT | Host port Compose publishes the relay on. 127.0.0.1:8878 keeps it private. | 8878 |
MANGOSSH_VERSION | Which relay image to run. The installer updates it on every upgrade. | The bundle's version |
BIND_ADDR | Listen address. Only for running the binary without Docker. | 0.0.0.0:8878 |
The three secrets can also come from files, which keeps them out of docker inspect and process listings. Set RELAY_AUTH_TOKEN_FILE, RELAY_CACHE_PASSPHRASE_FILE or RELAY_BROKER_CACHE_PASSPHRASE_FILE to a path, such as a mounted Docker secret under /run/secrets/, instead of the plain variable. The source repository's relay-server/docker-compose.yml has a commented example. Empty values count as unset, so a blank line in .env never turns off authentication.
Every device on a relay shares one RELAY_AUTH_TOKEN, and one passphrase protects each credential store. Anyone with the token can register agents and open Persistent Sessions on your relay. Give it only to people you trust, and change it if a device is lost: update .env, restart, then update each device's Auth token.
Backups and upgrades
Two things hold state, and you need both to restore:
/opt/mangossh/relay/.env, above allRELAY_BROKER_CACHE_PASSPHRASE. Without it, the stored credentials and recordings cannot be decrypted. Keep a copy in a password manager or safe, not next to the data backup.- The
/datavolume:broker_store.json(every provisioned PAM Broker host) andrdp_recordings/. Lose it and each broker host must be provisioned again by hand.
# a compressed copy of /data from the running container
sudo docker run --rm --volumes-from mangossh-relay -v "$PWD":/backup debian:bookworm-slim \
tar czf /backup/relay-data-$(date +%F).tgz /data
The files are already encrypted, but they are exactly what the broker passphrase unlocks, so store the archive as carefully as the passphrase. Persistent Sessions and Connect by ID pairings live only in memory and need no backup.
To upgrade, download the newer bundle and run its installer the same way. It keeps your .env and restarts the relay on the new image.
A restart drops every live relayed session and Persistent Session. Host agents reconnect by themselves. Agents running inside the app come back with a new Remote ID and password, while the Windows service keeps its own. Upgrade outside working hours.
Troubleshooting
| Symptom | Likely cause |
|---|---|
| The installer stops with an error | Its last line says why: Docker missing, not run with sudo, or a server that isn't x86-64. For a start-up failure, run sudo docker compose logs in /opt/mangossh/relay. |
| Compose says to set a random token in .env | RELAY_AUTH_TOKEN or RELAY_CACHE_PASSPHRASE is missing from /opt/mangossh/relay/.env. |
| Relay exits: “RELAY_BROKER_CACHE_PASSPHRASE (or RELAY_BROKER_CACHE_PASSPHRASE_FILE) is not set” | BROKER_TRUST_PUBKEY is set without the broker passphrase. Set it, or remove the trust key. |
| “BROKER_TRUST_PUBKEY must be 32 bytes” | You pasted the private key or a truncated value. Use the BROKER_TRUST_PUBKEY line from the vault server's .env, not TICKET_SIGNING_KEY. |
| “invalid or missing bearer token” | The device's Auth token does not match RELAY_AUTH_TOKEN. Check for a stray space or an old token. |
Devices cannot connect, /healthz works locally | DNS, the firewall, or the proxy. Open https://relay.yourteam.example/healthz from the device's network. |
| Connect by ID toggle turns itself off | The WebSocket upgrade is blocked. A proxy in front of the relay must pass Upgrade requests through. |
| Broker hosts vanished after a rebuild | BROKER_STORE_PATH pointed outside the /data volume. Keep the Compose defaults. |