Hermes Desktop has two operating modes: local mode (the agent runs on the same machine as the GUI) and remote mode (the agent runs somewhere else and Desktop connects to it over an API). This article covers remote mode against a Linux VPS.
This is the setup I run on my main machine. Agent stays up 24/7 on the VPS, gateways keep answering messages overnight, Desktop on the laptop is just a client. If the laptop dies I install Desktop on a new one and point it at the same VPS in under a minute.
Prerequisites
Before you start, you need:
- A Linux VPS with Hermes Agent installed. If you don't have one, see install Hermes Agent on Ubuntu.
- Hermes Desktop installed locally. Download from project releases.
- SSH access to the VPS already tested from the laptop.
- A decision: SSH tunnel only (private) or HTTPS exposed (public)?
Decide: SSH tunnel or HTTPS
You have two ways to make the VPS dashboard reachable from your laptop.
SSH tunnel (recommended for personal use)
Dashboard stays bound to 127.0.0.1 on the VPS. From your laptop you open an SSH tunnel that maps localhost:8642 on the laptop to localhost:8642 on the VPS. Traffic is encrypted by SSH. No public exposure.
HTTPS exposed (when messaging gateways need public endpoints)
Dashboard binds to 0.0.0.0 with basic auth. Nginx in front terminates TLS. Use this when you need webhooks coming in from external services like WhatsApp Meta API or Discord interactions endpoint.
I run mixed mode myself: SSH tunnel for daily Desktop use, HTTPS for the webhook endpoints. You'll see how to do both below.
Setup path 1: SSH tunnel
On the VPS, start the dashboard
By default Hermes binds the dashboard to 127.0.0.1:8642. That is what you want for the tunnel approach. Just start it:
hermes dashboard start
For 24/7 running put it under systemd. See run Hermes Agent with systemd.
On the laptop, open the tunnel
ssh -L 8642:127.0.0.1:8642 user@your-vps-ip
Keep this terminal open. Closing it closes the tunnel.
Point Desktop at it
Open Hermes Desktop. Pick remote mode. Enter:
- API URL:
http://127.0.0.1:8642 - API key: leave empty unless you also set up auth (you don't need it for SSH tunnel becuase the tunnel itself is the security)
Desktop validates the connection. If it opens the workspace, you're connected.
Setup path 2: HTTPS exposed dashboard
On the VPS, bind to all interfaces with auth
export HERMES_DASHBOARD_USERNAME="your-user"
export HERMES_DASHBOARD_PASSWORD="a-long-random-string"
hermes dashboard start --host 0.0.0.0 --auth basic
Important: never expose the dashboard publicly without auth. The dashboard can edit API keys and bot tokens. Open dashboard = stolen keys.
Put Nginx in front
Set up Nginx with TLS termination and proxy to localhost:8642. The full Nginx config including WebSocket upgrade headers (you need these for the chat stream) is in our Hermes Agent behind Nginx with HTTPS tutorial.
Verify with curl before opening Desktop
curl -s -u your-user:your-password https://your-domain/api/status | head -c 200
You should see JSON with auth_required: true and an auth_providers array that includes "basic". If not, the basic auth provider didn't initialise. Check that the env vars were exported in the same shell that started the dashboard.
Connect Desktop
Open Desktop in remote mode. Enter:
- API URL:
https://your-domain - API key: your basic auth password
Common error: WebSocket closes with code 4403 or 4401
Desktop pairs the chat over a WebSocket after initial HTTP auth succeeds. The WebSocket can fail even if HTTP works. Two specific close codes you might see in the Desktop dev console:
Code 4401: WebSocket ticket didn't authenticate
The dashboard issued a ticket for the WebSocket handshake but the ticket wasn't valid by the time Desktop sent it. Usually because the dashboard restarted between Desktop's HTTP auth and the WebSocket upgrade.
Code 4403: Request rejected by the chat WS guard
The dashboard's auth provider config is missing "basic" or the request didn't include the right auth header. Check:
curl -s http://127.0.0.1:8642/api/status | jq '.auth_required, .auth_providers'
You want true and an array including "basic". If the array is empty, the dashboard started before the env vars loaded. Restart with vars exported in the same shell that runs the start command, or move them into the systemd unit's Environment= directives.
Cloudflare Access in front of the dashboard
If you put Cloudflare Access (or another zero-trust proxy) in front, the first Desktop request gets a 302 redirect to login. Desktop follows but the Access cookie may not survive the WebSocket upgrade. Symptom: HTTP works, chat fails.
Fix: issue a Cloudflare Access service token, add it as a custom HTTP header in Desktop's advanced settings. The Cloudflare service token docs walk the issuance.
Per-profile remote hosts
One Desktop install can connect to multiple VPS backends through different profiles. You can have a Work profile pointed at production and a Dev profile pointed at staging. Switch with one click in the sidebar. Configure the remote URL inside each profile rather than globally.
Useful for keeping staging and production agents fully separate without two installs of Desktop.
What I wish I had known earlier
Auth env vars need to be in the same process
The dashboard auth env vars (HERMES_DASHBOARD_USERNAME, HERMES_DASHBOARD_PASSWORD) need to be loaded by the same process that runs hermes dashboard start. Setting them in ~/.bashrc doesn't help when the dashboard runs under a systemd service that doesn't read bashrc. Add them to the systemd unit's Environment= lines or export them in the same shell that runs the dashboard.
This cost me an hour the first time.
Test with curl before opening Desktop
If curl -u user:pass https://domain/api/status returns sensible JSON, Desktop will pair. If curl fails, fix that first. Desktop's error messages are better than they used to be but a clean curl test from the same machine still saves you guessing.
What comes next
Once Desktop is talking to the VPS, the natural follow ups are:
- Setting up a messaging channel (start with our Telegram gateway tutorial)
- Scheduled backups (our backups guide covers the SQLite-aware approach)
Hosting on LumaDock
The hybrid pattern needs a Linux VPS to host the agent. The LumaDock Hermes Agent template gives you that with Hermes preinstalled, the systemd unit configured and the dashboard ready to bind. Unmetered bandwidth on all plans, useful because Desktop in remote mode streams chat over WebSocket constantly. No setup fees, instant deploy. Full template details in our Hermes Agent complete guide.

