Setup Hermes with Slack: from install to first message

Hermes is one of the more capable self-hosted AI agents you can run today, especially if you want something that lives on a server, builds up memory and skills over time, and reaches you through whatever messaging app you actually open. Standing it up end to end, install, provider, Slack, persistence, takes more steps than any single doc page lays out, and a handful of them will leave you with a silently dead bot if you miss them.
This is a practical Hermes setup walkthrough, written after putting one together on a sandbox. No theory, no pitch. Pick a host, install, wire up Slack, keep it alive.
What Hermes actually is
Hermes is an autonomous agent that runs as a long-lived process. One Gateway connects to whatever messaging platforms you configure, manages your sessions, fires scheduled jobs, and talks to your chosen LLM provider. It also persists memory and auto-generated skills to disk, so it gets sharper at your specific patterns the longer it runs.
The framing that made it click for me: you are not installing a chatbot. You are running a personal AI service that you happen to talk to over Slack. Once that lands, the rest of the setup follows.
Prerequisites
Two things before you touch anything:
- An API key from a model provider, plus a model with at least 64K context. Anthropic, OpenAI, Google, OpenRouter, or any of the ~19 providers Hermes supports. Smaller-context models get rejected at startup, so do not bother with a 32K endpoint.
- A machine to run it on. The next step covers your options.
You also need Python 3.11 and Node 22 on the host, but depending on what you pick, the installer handles that.
Step 1: Choose your machine
The Gateway is a long-running process, and where it lives shapes the rest of the setup. Four reasonable picks:
A Mac mini or NUC at home. Cheap to run, snappy, fine for a personal agent. Con: dedicated hardware that has to stay on, and reaching it from outside your LAN needs work.
Your laptop. Decent for a first run-through. Con: closing the lid kills the Gateway, and the agent runs shell commands on the same filesystem you do everything else on.
A VPS (DigitalOcean, Hetzner, etc). Always on, isolated from your daily machine. Con: you handle the OS, Python, Node, firewall, persistence, and DNS yourself.
A Superserve sandbox. Firecracker microVM with pause/resume and snapshot persistence, isolated by default. The superserve/hermes template ships with Hermes, Python 3.11, Node 22, tmux, and ripgrep already in place, so the bootstrap collapses to nothing. Con: depends on a managed service, which is the usual tradeoff against running your own box.
The rest of this guide assumes a Superserve sandbox because the install is the shortest and the microVM is genuinely the right isolation model for an agent with terminal access. The Slack and config steps are identical on any other machine, you just install Python, Node, and Hermes yourself first.
Create a Superserve sandbox
- Go to
console.superserve.aiand click Create sandbox. - Pick the
superserve/hermestemplate. Python, Node, Hermes, tmux, ripgrep, and ffmpeg are all preinstalled. - Open Advanced Options and add your model provider key under Environment Variables, for example
ANTHROPIC_API_KEYorOPENROUTER_API_KEY. You will add the Slack tokens here too once you have them. - Create the sandbox and open its Terminal.
Confirm Hermes is on PATH:
bashhermes --version
If you went with a non-sandbox machine, run the one-line installer first. It pulls Python 3.11, Node 22, ripgrep, ffmpeg, and uv, then sets up the venv and the global hermes command:
bashcurl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash
Step 2: Wire up a model
Hermes has no default provider. You have to tell it which one to use before anything runs. The fastest path is hermes model, which walks you through provider selection and writes the choice to config:
bashhermes model
Providers light up in the picker based on which API keys it finds in your environment, so make sure the relevant key is set first. Pick a model with a 64K+ context window: Claude Opus or Sonnet 4.x, the GPT-5 series, and Gemini 2.5+ all qualify. Anything smaller gets refused at startup.
If you prefer the explicit path, set both values directly:
bashhermes config set model anthropic/claude-opus-4.7 hermes config set provider anthropic
Skip the optional skills, voice, and tool steps for now. Get one clean conversation running before layering features on. Sanity check it:
bashhermes chat
Send a quick "hi". If you get a reply, the provider side is good. If you see No inference provider configured. Run 'hermes model' to choose a provider and model, the env var did not land in this shell or the config does not have a provider set. Fix it before going further, since Slack will fail with the same error.
Step 3: Set up the Slack gateway
This is where most of the friction lives. Slack needs an app, two tokens, the right scopes, and event subscriptions. Miss any one of them and the bot stays silent with no useful error in chat.
Create the Slack app
- Go to
api.slack.com/appsand create a new app from scratch (not from a manifest). - Name it (
Hermesis fine) and pick your workspace.
Classic Slack apps were fully deprecated in March 2025, so the modern Bolt-based flow is the only one that works. If you find an older walkthrough telling you to use a classic app, ignore it and start fresh.
Enable Socket Mode
Socket Mode is the right choice for almost every Hermes setup. It uses an outbound WebSocket from your host to Slack, so you do not need a public URL or any inbound port open. That matters on sandboxes and home machines, where exposing ports is a hassle.
- In the app settings, go to Socket Mode and toggle it on.
- Go to Basic Information, scroll to App-Level Tokens, and generate a token with the
connections:writescope. This is yourxapp-token. Save it somewhere safe.
Add bot scopes
Go to OAuth & Permissions, scroll to Bot Token Scopes, and add at minimum:
textapp_mentions:read channels:history chat:write groups:history im:history im:read im:write mpim:history
The two *:history scopes are the ones people skip most often. Without them, the bot works fine in DMs but silently ignores everything posted in channels. That is the most common setup failure on Slack, and the symptom is misleading because the bot looks healthy.
Subscribe to events
Skip this step and Socket Mode connects without complaint, but Slack has no idea which events to forward, so nothing actually reaches Hermes. This is the second most common silent failure.
- Go to Event Subscriptions and toggle Enable Events on.
- Under Subscribe to bot events, add:
textapp_mention message.channels message.groups message.im
- Save changes.
Install and grab the bot token
Install the app to your workspace. Copy the xoxb- Bot User OAuth Token from the OAuth & Permissions page.
Important: any time you change scopes or events after this, Slack requires a reinstall, and the reinstall is what actually grants the new permissions. If you skip the reinstall, your "new" scopes are not active. When in doubt, reinstall.
Store tokens as environment variables
Two tokens, both treated as secrets:
SLACK_BOT_TOKEN, thexoxb-bot token from OAuth & Permissions.SLACK_APP_TOKEN, thexapp-app-level token from Basic Information.
On a Superserve sandbox, add both under Environment Variables in the console, alongside your model key. Setting them there injects them into the sandbox environment itself, so every new shell and every process started in the sandbox sees them. This matters more than it sounds, see the env var note in the gotchas.
On any other machine, persist them in ~/.hermes/.env with strict permissions. The folder may not exist yet:
bashmkdir -p ~/.hermes cat >> ~/.hermes/.env <<'EOF' SLACK_BOT_TOKEN=xoxb-your-token SLACK_APP_TOKEN=xapp-your-token EOF chmod 600 ~/.hermes/.env
The single-quoted 'EOF' matters: it tells bash not to expand $ inside the block, which is what you want for opaque token strings.
Allow the right users
By default, the gateway denies every incoming message as a safety measure. You have to explicitly allowlist either specific users or everyone. For a team bot in a shared channel, opening it up is what you usually want:
bashecho 'GATEWAY_ALLOW_ALL_USERS=true' >> ~/.hermes/.env
If you would rather keep it tight, grab the Slack Member IDs of the people who should have access (profile menu, three dots, Copy member ID) and list them instead:
bashecho 'SLACK_ALLOWED_USERS=U01ABC2DEF3,U02DEF4GHI5' >> ~/.hermes/.env
Configure Slack behavior
Behavioral config lives in ~/.hermes/config.yaml. The defaults are sane for most cases. The block I ran with:
bashcat > ~/.hermes/config.yaml <<'EOF' platforms: slack: require_mention: true extra: reply_in_thread: true EOF
A few notes on what these flags actually do:
require_mention: trueis the default and the right setting for a channel bot. The bot only replies when someone@-mentionsit. Drop this and you risk auto-engagement, where Hermes remembers being addressed in a thread and starts following up on every reply.reply_in_thread: truekeeps channel responses inside threads. Hermes streams tool progress as it works (๐ป ls -la...,๐ web_search...), so without threading, a single question can spam a channel with five messages. Threads keep the channel readable.- Slack sessions are keyed by thread, not by user. This is specific to Slack, Telegram and Discord key sessions by user. Multiple teammates in the same thread share one conversation, one context, one history. It is the right behavior for a team bot, and it is the main reason to put Hermes on Slack over the other platforms.
Set a home channel
Hermes uses a "home channel" for proactive messages: cron output, cross-platform notifications, scheduled task results. On your first message the gateway will hint about this. Set it either via env var:
bashecho 'SLACK_HOME_CHANNEL=C01ABC2DEF3' >> ~/.hermes/.env
(Right-click the channel in Slack, View channel details, scroll to the bottom for the channel ID.)
Or in chat, from inside the channel you want as home:
text/hermes sethome
A small footgun here: the onboarding hint currently tells you to type /sethome, which does not work on Slack. The Slack adapter registers everything under a single /hermes parent command, so the actual invocation is /hermes sethome. If /sethome returns "the app did not respond", that is why. There is a tracking issue open against this.
If you do not need cron or proactive output, you can ignore the hint entirely. The bot still works fine without a home channel set; you just lose scheduled-job delivery.
Step 4: Run doctor
hermes doctor checks your install, config, and provider auth for the obvious mistakes. Run it before starting the gateway:
bashhermes doctor
It catches missing provider credentials, unreachable model endpoints, broken backends, and a handful of other footguns. It is also the first thing to run any time the bot starts behaving strangely later. Most misconfigurations show up here.
On a sandbox without systemd, doctor also notes that service installation is unavailable and tells you to run the Gateway as a foreground process. That is expected. The next step handles persistence with tmux.
Step 5: Start the gateway
If your machine has systemd available (most VPS images and home Linux installs), the cleanest path is the built-in service installer:
bashhermes gateway install
This registers a user systemd service that auto-restarts on crash and survives reboots. Verify with:
bashsystemctl --user status hermes-gateway journalctl --user -u hermes-gateway -f
On a Superserve sandbox, the Firecracker rootfs does not run systemd as a user service host, so hermes gateway install refuses with:
textService installation not supported on this platform. Run manually: hermes gateway run
That is fine. Use tmux instead:
bashtmux new -s hermes hermes gateway run
Watch the logs. You want to see Socket Mode connecting cleanly and your channel resolving by ID. Then detach without killing the gateway: press Ctrl+B, release, then press D. Back at a normal shell, the gateway keeps running.
To come back later:
bashtmux ls # list sessions tmux attach -t hermes # reattach
One subtlety: if you already started hermes gateway run directly in your shell and now want to move it into tmux, you cannot. Ctrl+C it first, then start it fresh inside tmux. A few seconds of downtime.
If tmux is not available either, nohup works as a last resort:
bashnohup hermes gateway run > ~/.hermes/gateway.log 2>&1 & disown
disown removes the process from the shell's job table so closing the terminal does not send it SIGHUP. Logs land in ~/.hermes/gateway.log. Stop it with pkill -f "hermes gateway".
Step 6: Test in Slack
In Slack:
- Invite the bot to a channel:
/invite @Hermes. The bot does not auto-join, you have to invite it to every channel where it should listen. - Mention it:
@Hermes hello.
If it replies inside a thread, you are done. From there, anyone in that thread can keep talking to it without re-mentioning. Outside threads, every new request needs a fresh @-mention.
For first-time DM access from someone who is not on the allowlist, Hermes uses a one-time pairing code flow. The user gets a code in chat, you approve it from the terminal:
bashhermes pairing list hermes pairing approve slack <code>
This only matters if you stuck with SLACK_ALLOWED_USERS instead of GATEWAY_ALLOW_ALL_USERS=true.
A few gotchas worth knowing
Channels go quiet after scope changes. If the bot replies in DMs but not in channels, the cause is almost always missing channels:history and groups:history scopes, plus forgetting to reinstall the app after adding them. Both have to be fixed. One alone is not enough.
Env vars and the running gateway. A process only sees the environment variables that existed when its shell started. The gateway lives inside a tmux session, and that tmux shell captured its environment at creation time. If you add or change a token after that, the already-running gateway will not see the new value. After any token change, restart the gateway: inside the tmux session, Ctrl+C the gateway, then hermes gateway run again. On a Superserve sandbox, console-level env vars get injected into new shells, but the gateway process itself still needs a restart to pick them up.
Pause and resume kills the WebSocket. When the microVM pauses (or your laptop sleeps), the Slack WebSocket dies. The Slack SDK reconnects automatically on resume, but messages sent during the pause window are lost. Slack does not replay socket events. If your sandbox pauses aggressively, configure it to stay awake, or accept the lost-messages-while-paused tradeoff for a personal bot.
Process supervision matters. Without systemd, your tmux session is the only thing keeping the gateway alive. If the host reboots, the tmux session is gone, the gateway is gone, and your 24/7 assistant is actually a "until next reboot" assistant. For a personal sandbox that rarely restarts, manually restarting is fine. For anything you want others to depend on, drop the start command in ~/.bashrc or set up a real init system.
Blast radius. The moment Hermes is on Slack, anyone in the allowed user list (or anyone in the channel, if you flipped GATEWAY_ALLOW_ALL_USERS=true) can issue commands to a process with terminal access. Keep approval prompts on for destructive actions. If you are exposing it to a wider team, lock the terminal backend down with hermes config set terminal.backend docker, or lean on the microVM as your isolation boundary and disable tools you do not need.
Wrapping up
That is a full Hermes setup, from picking a host through a working Slack channel and a gateway running in tmux. The pattern across every failure mode I hit was the same: a silent breakage with no error in chat, and the actual cause buried in scopes, event subscriptions, env var scope, or process lifecycle.
Run hermes doctor whenever something feels off, it catches most issues on its own. And remember that a running gateway only sees the environment it started with, so restart it after any token change.
For more on the Superserve side, see the Hermes integration doc.