Skip to content
  • MangoFly

    A self-hosted WireGuard mesh. Devices connect straight to each other; the coordination server is one binary and a SQLite file, and never sees their traffic.

    encrypted WireGuard · peer to peerLaptopbehind home NATServerin a datacentrePhoneon mobile datacoordination serverone binary · one SQLite filecontrol plane only (TLS)keys · tunnel addresses · peer lists · sealed ICE candidatesholds no private keys · carries no traffic · cannot decryptdatacontrol
  • MangoDock

    Docker management with nothing on the hosts. Reaches each daemon over an ordinary SSH session — no agent to install, no port to open.

    The MangoDock dashboard showing three host cards with container state counts, CPU and memory gauges, a usage history and recent events
  • MangoWiFi

    A Wi-Fi 6/7/8 test bench. One binary runs as Console or Agent either side of the access point under test, measuring latency under real load.

    AP under testWi-Fi 6 / 6E / 7Agentstation side · real radioLAN receiveriperf3 -sConsoleUI · orchestrates · probes
  • Blog
  • Nothing phones home

    No telemetry, no analytics, no crash reporter, no account login. Check it with a packet capture on your own network.

    Download MangoSSH
  • Project
  • Download
  • 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:

    FeatureWhat the relay doesAlso needs
    Connect by IDPairs a viewer with a registered host by Remote ID and password. Carries the session when no direct path forms.Nothing else
    SSH Persistent SessionsHolds 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 BrokerStores 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 accessServes 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 recordingRecords 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.

    MangoSSH devices, host agents and browsers connect over HTTPS to Caddy, which forwards to the relay. For brokered sessions the relay connects on to the target servers. MangoSSH devices viewers, operators Host agents Connect by ID Browsers share links Caddy (HTTPS) :443 relay :8878 Targets SSH, RDP, VNC
    Everyone dials in to the relay. The relay dials out only for Persistent Sessions and brokered sessions, directly or through a host agent.
    The relay is not a zero-knowledge server

    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.

    1. Install Docker and the Compose plugin, if the machine doesn't have them.

      curl -fsSL https://get.docker.com | sh
    2. Point DNS at the machine. Create an A record such as relay.yourteam.example and wait until it resolves. The certificate can only be issued once it does.

    3. Download the bundle.

      curl -fLO https://downloads.mangossh.com/mangossh/latest/mangossh-relay-linux-x64.tar.gz
    4. Check 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 -c would look for the versioned name and report it missing.

      sha256sum mangossh-relay-linux-x64.tar.gz
    5. Unpack and install.

      tar -xzf mangossh-relay-linux-x64.tar.gz
      cd mangossh-relay-*-linux-x64
      sudo ./install.sh --domain relay.yourteam.example
    6. Check it from outside: curl https://relay.yourteam.example/healthz should print ok.

    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.

    Linux x86-64, and internet during install

    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.

    Always use a domain over the internet

    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

    1. Open Settings → Remote → Relay Server.

    2. Enter the Relay URL, such as wss://relay.yourteam.example, and paste the auth token the installer printed (RELAY_AUTH_TOKEN in /opt/mangossh/relay/.env) into Auth token.

    3. Click Save. The row under Saved relay shows the URL and a masked token.

    4. 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 formUse
    wss://relay.yourteam.exampleThe normal choice, behind the HTTPS proxy.
    wss://relay.yourteam.example/v1/sessionAlso accepted. MangoSSH strips any /v1/… path and adds the right one for each feature.
    ws://192.168.1.20:8878Testing 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 .env and 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 run sudo docker compose up -d in /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.

    VariableWhat it doesDefault
    RELAY_AUTH_TOKENShared bearer token every MangoSSH device presents to use Connect by ID, Persistent Sessions and browser share links. Required.None
    RELAY_CACHE_PASSPHRASEEncrypts credentials cached for Persistent Sessions. Required.None
    BROKER_TRUST_PUBKEYThe 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_PASSPHRASEEncrypts PAM Broker credentials and RDP recordings. Required when BROKER_TRUST_PUBKEY is set.Unset
    BROKER_STORE_PATHFile holding provisioned broker credentials. Compose sets it inside the data volume./data/broker_store.json
    RDP_RECORDINGS_PATHFolder for server-side RDP recordings. Compose sets it inside the data volume./data/rdp_recordings
    IDLE_TIMEOUT_SECSHow long a Persistent Session is kept with no device attached.900 (15 min)
    PUBLIC_PORTHost port Compose publishes the relay on. 127.0.0.1:8878 keeps it private.8878
    MANGOSSH_VERSIONWhich relay image to run. The installer updates it on every upgrade.The bundle's version
    BIND_ADDRListen 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.

    One token for everyone

    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 all RELAY_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 /data volume: broker_store.json (every provisioned PAM Broker host) and rdp_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

    SymptomLikely cause
    The installer stops with an errorIts 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 .envRELAY_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 locallyDNS, the firewall, or the proxy. Open https://relay.yourteam.example/healthz from the device's network.
    Connect by ID toggle turns itself offThe WebSocket upgrade is blocked. A proxy in front of the relay must pass Upgrade requests through.
    Broker hosts vanished after a rebuildBROKER_STORE_PATH pointed outside the /data volume. Keep the Compose defaults.