Install Hermes on a Raspberry Pi

A native install on Raspberry Pi OS, over SSH. Do the steps yourself, or hand this page to an AI coding agent and let it do most of the work.

You ssh Raspberry Pi hermes

Step 1 — What you need

  • Raspberry Pi — arm64 (aarch64), Raspberry Pi OS, reachable over SSH
  • sudo on the Pi — passwordless keeps the install unattended
  • Outbound internet from the Pi
  • ChatGPT account — or another provider Hermes supports

Step 3 — Preflight

Confirm the architecture, sudo, and a route to the installer:

ON THE PI
$ uname -m                              # expect: aarch64
$ sudo -v                               # confirm you can elevate (installer uses apt)
$ curl -fsSI https://hermes-agent.nousresearch.com/install.sh >/dev/null && echo net-ok
CautionIf sudo asks for a password, the installer’s apt step stalls. Cache it first (sudo -v, keep the session open), or pre-install the packages so the installer never needs sudo:
sudo apt-get install -y git curl xz-utils gcc python3-dev libffi-dev ripgrep ffmpeg

Step 4 — Install

binaries only, no wizard

One pipe installs everything. --skip-setup skips the interactive sign-in wizard; the sign-in is Step 5.

ON THE PI
$ curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash -s -- --skip-setup

It lands the CLI at ~/.local/bin/hermes and all state under ~/.hermes/. Confirm:

ON THE PI
$ ~/.local/bin/hermes version        # prints a version number
For agentsAlways call the binary by full path: ~/.local/bin/hermes. A non-interactive ssh <pi> '…' shell has no ~/.local/bin on PATH, and source ~/.bashrc won’t fix that (bashrc returns early when non-interactive). bash -lc also works.

Step 5 — Sign in

YOURS

The sign-in uses your account and a browser, so it’s yours. The default path is OpenAI Codex (ChatGPT); any provider Hermes supports works:

ON THE PI
$ ~/.local/bin/hermes auth add openai-codex     # suggested: ChatGPT sign-in — prints a URL + code
open  https://auth.openai.com/codex/device   # approve in the browser

For another provider: ~/.local/bin/hermes auth add <provider>. Leave the model to Step 6.

For agentsStop here; this needs the human’s account and a browser. Ask them to run it, then wait. When they say they’re signed in, confirm with ~/.local/bin/hermes auth status <provider> (reads logged in) and continue with Step 6.

Step 6 — Pair the model, then verify

See what you signed in with:

ON THE PI
$ ~/.local/bin/hermes auth list
openai-codex (1 credentials):
  #1  openai-codex-oauth-1 oauth   device_code ←

Set the default model to match (here: provider openai-codex, model gpt-5.5):

ON THE PI
$ ~/.local/bin/hermes config set model.provider openai-codex
✓ Set model.provider = openai-codex in /home/<user>/.hermes/config.yaml
$ ~/.local/bin/hermes config set model.default gpt-5.5
✓ Set model.default = gpt-5.5 in /home/<user>/.hermes/config.yaml

Verify:

ON THE PI
$ ~/.local/bin/hermes chat -q 'reply with the word ok'
ok

Step 7 — Talk to Hermes

Hermes is running; choose how you’ll reach it.

For agentsAsk the user how they want to talk to Hermes — the web dashboard, terminal chat, and/or an iMessage number — then set up the ones they pick. Don’t assume they want just one, or none.
  • Terminal chat — chat right in an SSH session. Way 1, below.
  • Web dashboard — a browser UI, tunneled to the Mac. Way 2, below.
  • An iMessage number — want to text your agent from your phone? Give your Hermes agent a phone number.

Way 1 — terminal chat over SSH

ON THE PI
$ hermes                       # interactive chat (in your own ssh session)
$ ~/.local/bin/hermes chat -q 'your prompt'   # one-shot (full path over ssh)

Way 2 — the web dashboard, tunneled to the Mac

MAC localhost:9119 ssh tunnel PI · dashboard 127.0.0.1:9119

Hermes has a web dashboard. On a headless Pi, keep it on loopback and skip the browser auto-open:

ON THE PI
$ ~/.local/bin/hermes dashboard --host 127.0.0.1 --port 9119 --no-open

With the dashboard running, forward the port from the Mac:

ON THE MAC
$ ssh -f -N -L 9119:localhost:9119 pi   # -f backgrounds it; -N = tunnel only, no shell
$ curl -s -o /dev/null -w '%{http_code}\n' http://localhost:9119/   # expect: 200
# then open in your Mac browser:  http://localhost:9119

The dashboard is reachable only while that ssh process runs. -f backgrounds the tunnel until you kill it (pkill -f '9119:localhost:9119') or reboot. Without -f it prints nothing; that’s normal for -N, so leave the window open.

CautionFirst boot is slow. The dashboard spends about a minute building its frontend on first run (the service reads active right away), and until then the curl probe prints 000 or nothing. Re-run it until it prints 200.
CautionKeep it loopback + tunnel. A non-loopback bind trips an auth gate; --insecure bypasses it and belongs only behind a tunnel or VPN.

Keep the dashboard alive on the Pi

The dashboard dies at logout. Run it as a user service:

ON THE PI
$ mkdir -p ~/.config/systemd/user
$ cat > ~/.config/systemd/user/hermes-dashboard.service <<'EOF'
[Unit]
Description=Hermes dashboard
After=network-online.target
 
[Service]
ExecStart=%h/.local/bin/hermes dashboard --host 127.0.0.1 --port 9119 --no-open
Restart=on-failure
 
[Install]
WantedBy=default.target
EOF
$ loginctl enable-linger "$USER"          # survive logout / reboot (easy to forget)
$ systemctl --user daemon-reload
$ systemctl --user enable --now hermes-dashboard.service
$ systemctl --user is-active hermes-dashboard.service   # active

This keeps the dashboard up across Pi reboots; the LaunchAgent below does the same for the tunnel on the Mac.

Keep the tunnel alive on the Mac

The ssh -f -N tunnel dies at logout. To keep http://localhost:9119 up across reboots, run the tunnel as a LaunchAgent:

CautionPasswordless SSH must already work — launchd can’t type a password. ssh pi true should return without prompting; Step 2’s ssh-copy-id pi set this up.
For agentsThis installs on the human’s Mac, not the Pi. After launchctl bootstrap, run the curl probe, then tell the human the dashboard is live at http://localhost:9119.
ON THE MAC
$ mkdir -p ~/Library/LaunchAgents
$ cat > ~/Library/LaunchAgents/co.plow.hermes-tunnel.plist <<'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key><string>co.plow.hermes-tunnel</string>
  <key>ProgramArguments</key>
  <array>
    <string>/usr/bin/ssh</string>
    <string>-N</string>
    <string>-o</string><string>ServerAliveInterval=30</string>
    <string>-o</string><string>ServerAliveCountMax=3</string>
    <string>-o</string><string>ExitOnForwardFailure=yes</string>
    <string>-L</string><string>9119:localhost:9119</string>
    <string>pi</string>
  </array>
  <key>RunAtLoad</key><true/>
  <key>KeepAlive</key><true/>
</dict>
</plist>
EOF
$ launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/co.plow.hermes-tunnel.plist   # load it now

RunAtLoad starts the tunnel at login; KeepAlive restarts ssh if it dies; ServerAliveInterval=30 / ServerAliveCountMax=3 kill a hung connection within about 90 seconds; ExitOnForwardFailure=yes fails loudly if port 9119 is taken. No autossh needed.

Turn it off:

ON THE MAC
$ launchctl bootout gui/$(id -u)/co.plow.hermes-tunnel
$ rm ~/Library/LaunchAgents/co.plow.hermes-tunnel.plist

Either way the dashboard is exposed only on the Mac’s loopback (127.0.0.1:9119). On Linux, use a systemctl --user unit instead of a LaunchAgent.

Optional next step: Hermes is running. Want to text it from your phone? Give your Hermes agent a phone number.

Step 8 — Sharp edges

Read once before you start.

EdgeWhat to do
Model / provider mismatchIf hermes chat errors about the model, the pairing is off. Step 6 fixes it: read the provider from hermes auth list, then set hermes config set model.provider and hermes config set model.default together.
hermes not found over SSHA non-interactive ssh <pi> 'hermes …' shell has no ~/.local/bin on PATH, and source ~/.bashrc won’t help (bashrc returns early). Use ~/.local/bin/hermes, or wrap in bash -lc.
The installer runs a setup wizardBare install.sh ends in an interactive wizard. --skip-setup installs binaries only; sign in in Step 5.
Sudo is only for apt packagesOnly the installer’s apt step needs sudo. Cache it (sudo -v) or pre-install the packages; the rest runs password-free.
Service dies when you log outWithout loginctl enable-linger "$USER", a systemctl --user service stops at logout. tmux and nohup die on reboot regardless.
Dashboard exposureDefault bind is loopback (127.0.0.1:9119). Don’t rebind to 0.0.0.0; tunnel instead. Non-loopback trips an auth gate by design.
~/.hermes/ is everythingConfig, auth, sessions, skills, memories: one directory. It’s the backup unit, and removing it is most of an uninstall.

Step 9 — Uninstall

Two halves: the tunnel on the Mac, then Hermes on the Pi.

On the Mac — stop the tunnel

Unload the LaunchAgent:

ON THE MAC
$ launchctl bootout gui/$(id -u)/co.plow.hermes-tunnel 2>/dev/null; rm -f ~/Library/LaunchAgents/co.plow.hermes-tunnel.plist

A hand-started tunnel (ssh -f -N) dies with pkill -f '9119:localhost:9119' instead.

On the Pi — remove Hermes

Stop the service, then remove the install and the PATH line the installer added:

ON THE PI
$ systemctl --user disable --now hermes-dashboard.service
$ rm -f ~/.config/systemd/user/hermes-dashboard.service
$ systemctl --user daemon-reload
$ loginctl disable-linger "$USER"
$ pkill -f "hermes-agent/venv/bin/hermes" || true
$ rm -rf ~/.hermes ~/.local/bin/hermes ~/.local/bin/node ~/.local/bin/npm ~/.local/bin/npx
$ sed -i '/# Hermes Agent/,+1d' ~/.bashrc
CautionDeleting ~/.hermes removes config, auth, and all sessions. No undo.

Left in place, harmless: the apt packages (git curl xz-utils gcc python3-dev libffi-dev ripgrep ffmpeg) and the uv caches (~/.local/share/uv, ~/.cache/uv).