Set up hermes-webui The legacy guide
Hermex is a native iPhone and iPad client for a server you run yourself: the open-source hermes-webui (MIT). Three moves and you’re steering it from your phone — about 15 minutes, no Hermex account needed.
This guide is for hermes-webui servers. Hermex 1.x connects only to webui. Hermex 2.0 connects to the Hermes dashboard directly: see the setup guide. webui servers keep working in 2.0, and Move to Hermes, in the app, moves a webui server’s chats and drafts to a Hermes server.
What you’ll need
- A machine you control to run the server — macOS, Linux, or Windows via WSL2 — with Python 3.11+ and git.
- A Hermes Agent setup — if it’s missing, the bootstrap below offers to install it for you.
- For the recommended path, Tailscale on both the server and your iPhone or iPad.
- An iPhone or iPad with Hermex installed (free on the App Store).
Shortcut: Hermex’s onboarding includes an agent setup prompt. Send it to your Hermes agent and it handles steps 01 and 02 on the Tailscale path, then replies with the URL to enter in step 03. In Hermex 2.0, onboarding opens on the Hermes dashboard’s prompt; tap Using Hermes Web UI instead? under it for this one.
Run your server
Clone hermes-webui and run its bootstrap. It finds (or installs) Hermes Agent, sets up a Python environment, starts the server, and walks you through first-run onboarding:
git clone https://github.com/nesquena/hermes-webui.git
cd hermes-webui
python3 bootstrap.pyBy default the server listens on 127.0.0.1:8787 — local-only until you expose it in step 02. To relaunch later, run ./start.sh from the repo.
Set a password
Auth is off by default for localhost, but you’re about to put this on a network — set a password before exposing it (Hermex asks for it when you connect). Either flip it on in the server’s Settings panel, or via environment variable:
HERMES_WEBUI_PASSWORD=your-secret ./start.shKeep it running (optional)
For an always-on box, the bundled launcher runs it as a background daemon:
./ctl.sh start # background daemon
./ctl.sh status # uptime, bound host/port, health
./ctl.sh stopVerify
curl http://127.0.0.1:8787/healthMake it reachable
Your iPhone or iPad needs a way to reach the server. Pick one path — each ends with a URL you’ll type into Hermex:
Option A — Tailscale Serve (private, recommended)
Tailscale puts your server and your iPhone or iPad on the same private WireGuard network — nothing is exposed to the public internet, and it’s the path Hermex’s own onboarding walks you through. Install Tailscale on both devices and sign in to the same tailnet. Leave the server on 127.0.0.1 with the password from step 01, then publish it to your tailnet over HTTPS:
tailscale serve --bg 8787
tailscale serve status # shows your https://….ts.net URLYour server URL is the https://<machine>.<tailnet>.ts.net address that tailscale serve status prints. The first time, Tailscale may ask you to enable HTTPS certificates for your tailnet. Serve answers on HTTPS port 443 at the root path — if this machine already serves something there, check the status first so you don’t replace it. On Linux, if it reports Access denied: serve config denied, run it with sudo or allow your user once with sudo tailscale set --operator=$USER.
Fallback — the server’s Tailscale IP
If Serve isn’t available on your tailnet, start the server listening on all interfaces instead, with a password:
HERMES_WEBUI_HOST=0.0.0.0 HERMES_WEBUI_PASSWORD=your-secret ./start.sh
tailscale ip -4 # your server's Tailscale addressYour server URL is http://<tailscale-ip>:8787. WireGuard still encrypts the traffic, but binding to 0.0.0.0 also answers on your local network, not just the tailnet — that password is doing real work.
Option B — Cloudflare Tunnel (public HTTPS hostname)
Cloudflare Tunnel dials out from your machine to Cloudflare’s edge, so nothing is port-forwarded and the server can stay bound to 127.0.0.1. TLS terminates at Cloudflare — Hermex sees real HTTPS. To try it instantly (prints a temporary trycloudflare.com URL that changes on every run):
brew install cloudflared # or see Cloudflare's install docs
cloudflared tunnel --url http://localhost:8787For a permanent hostname on a domain you’ve added to Cloudflare, create a named tunnel:
cloudflared tunnel login
cloudflared tunnel create hermes
cloudflared tunnel route dns hermes hermes.yourdomain.com
cloudflared tunnel run --url http://localhost:8787 hermesYour server URL is https://hermes.yourdomain.com. The hostname is public, so the password from step 01 is required here, not optional.
Connect Hermex
- Install Hermex from the App Store and open it on your iPhone or iPad.
- Enter your Server URL from step 02 — your ts.net address, your Cloudflare hostname, or the Tailscale IP fallback.
- Tap Test Connection. Hermex pings your server’s
/healthendpoint and confirms it can reach it. - Enter the password you set in step 01 and sign in. Your server URL is stored in the iOS Keychain, on your device; the password isn’t stored after login — Hermex talks only to your server. Optional push notifications travel encrypted through an open-source relay; see the privacy policy.
Troubleshooting
The app can’t reach the server
Work outward. On the server itself, check curl http://127.0.0.1:8787/health (and ./ctl.sh status if you run the daemon). If that works, open your server URL’s /health in another device’s browser. Whichever hop fails is the one to fix.
The tunnel URL stopped working
Quick tunnels (trycloudflare.com) get a new URL every time cloudflared restarts — switch to a named tunnel for a stable hostname. For named tunnels, check cloudflared tunnel info hermes and make sure cloudflared is still running.
The Tailscale URL doesn’t respond
Confirm both devices are signed into the same tailnet and show as connected in the Tailscale app. On the server, tailscale serve status should list your ts.net URL proxying to 127.0.0.1:8787 — if it doesn’t, run tailscale serve --bg 8787 again. On the IP fallback, the server must be started with HERMES_WEBUI_HOST=0.0.0.0 — the default binding is localhost-only, which the tailnet can’t reach.
Sign-in keeps failing
The password in Hermex must match HERMES_WEBUI_PASSWORD on the server (or the one set in its Settings panel). If you set the variable inline, remember it only applies to that launch — daemon restarts need it set again or persisted in the server’s .env.
Hermex says the server uses single sign-on or passkeys
Hermex signs in with a password. If your hermes-webui only offers single sign-on (OIDC) or passkeys, the app can’t complete sign-in yet — enable password sign-in on the server (HERMES_WEBUI_PASSWORD) to connect.
The URL opens the Hermes dashboard
Hermex 1.x needs hermes-webui, a separate server from the dashboard that comes with Hermes Agent. If your URL opens the dashboard instead, run hermes-webui (step 01) and point Hermex at its address. Hermex 2.0 connects to the dashboard directly: follow the setup guide instead.
Port 8787 is already taken
Find the culprit with lsof -i :8787, or move the server with HERMES_WEBUI_PORT=9000 ./start.sh (update your Serve port or tunnel to match).
Long responses cut off mid-stream
The server keeps streams alive with heartbeats, but Cloudflare’s free tier can drop a connection that produces nothing for ~100 seconds. Hermex reconnects on its own and resumes the stream from the last event it received — nothing is lost on the server.
Still stuck? Ask in the Hermex Discord or message me on X — include what /health returns locally and which path (Tailscale or Cloudflare) you’re on. Found a bug in the app? Open an issue on GitHub. Server-side issues live upstream at nesquena/hermes-webui.
