Self-hosted AI agents are getting really good, and Hermes Agent is one of my favorites right now. It runs on your own hardware, manages its own models, tools, skills, and memory, and comes with a full web dashboard. The catch is the same one every self-hosted app has: how do you access it from your laptop or phone without punching holes in your firewall?
That's what we're building today. By the end of this guide you'll have Hermes Agent running as an always-on service on a Linux server, with two web interfaces you can reach from anywhere over NetBird. No public IPs, no open ports, no exposing an AI agent that can run tools and edit files to the entire internet (please don't do that).
We'll do this in two stages:
- Direct access over the NetBird peer IP, locked down with NetBird groups and access policies.
- Friendly HTTPS hostnames through NetBird Reverse Proxy with NetBird-Only Access, so you get instead of remembering an IP and port.
Do note that this is a Linux and systemd guide. I'm using Fedora-style commands first with Debian and Ubuntu alternatives where it matters. Replace the example domains, usernames, groups, and passwords with your own.
Why Hermes?
Fair question, and one I asked myself. The apps from OpenAI and Anthropic are great, but they're their apps, running on their servers, tied to their models and their accounts. Hermes flips that around.
For starters, Hermes is free and open source. You're not logging into any large organization, and your agent isn't living on someone else's infrastructure. It runs on your hardware, on your terms.
It also doesn't lock you into a single provider. We already saw this in the setup, you can point it at ChatGPT, Grok, Nous Portal, or plenty of others, and switching is just re-running the wizard. You can even mix in local models if you've got the hardware for it, so your agent can lean on a big cloud model for heavy lifting and a local one for quick or private stuff. That flexibility is really the whole pitch.
Beyond the freedom, there are some genuinely cool features that make Hermes feel less like a chatbot and more like an actual agent that grows with you:
- Skills that self-improve. Skills are basically procedural memory. Hermes builds them from experience, reuses them, and refines them as it goes. They're portable and shareable too, so the community trades them around.
- The command. This one's my favorite. Point it at a directory, a docs URL, a past conversation, or just some notes you paste in, and Hermes distills the whole thing into a reusable skill you can invoke later as its own slash command. No hand-writing config, it writes the skill for you.
- Persistent memory. It actually remembers things across sessions instead of starting from zero every chat.
- Dead-simple automations. There's a built-in cron system that can deliver results to any platform you've connected, so scheduled tasks and reports just show up where you want them.
- MCP support and 60+ built-in tools. Web search, browser control, image generation, and more, plus you can extend it with MCP servers.
Add it all up and you get an agent that gets more capable the longer you run it, entirely under your own roof. That's exactly the kind of thing worth putting behind NetBird instead of the open internet.
What We're Building
Here's the full picture:
You might be wondering why we're running two web interfaces. They solve different problems. The official Hermes dashboard is the canonical management surface and the remote backend for Hermes Desktop, and its Chat page embeds the real Hermes TUI. The community hermes-webui project is an independent, ChatGPT-style interface with a much nicer daily chat and mobile experience. Both talk to the same underlying Hermes installation, so you're not maintaining two brains, just two front doors.
One thing before we dive in: keep the application logins enabled even behind NetBird. These interfaces can run tools, edit files, and access provider credentials. The trust model here is layered. NetBird controls which peers can connect, NetBird-Only Access controls who can use each private hostname, and each app keeps its own login. Three layers, and we're keeping all three.
Installing Hermes Agent
First, a few basics on the host. On Fedora:
On Debian or Ubuntu:
Now go ahead and install Hermes with the official installer, as a normal unprivileged user. Do not use here, we want the per-user layout:
If this is a headless box and you don't need browser automation, add to the end (). The installer manages its own Python, Node.js, and other dependencies, so you don't need to build an environment first.

Reload your shell and verify:
Configuration lands in , with secrets in . Lock that file down:
We'll connect a model provider in the next section. Before we touch anything network related, get the basics good to go first, because if the CLI doesn't work locally, remote access is going to be miserable to troubleshoot.
Connecting a Model
An agent with no model behind it doesn't do much, so let's give it a brain. The wizard walks you through picking a provider, and you've got a few good options depending on how you already pay for AI.

My personal favorite is signing in with an existing subscription over OAuth instead of pasting in an API key. I use my ChatGPT subscription this way, so I'm not paying separate per-token API costs on top of a plan I already have. When the wizard asks how you want to authenticate, choose the OAuth or sign-in option rather than the API key one, and it'll send you through a browser login. Do note that on a headless server you'll want to do this OAuth step somewhere you have a browser, or copy the login URL over.
You can do the exact same thing with Grok if that's your subscription of choice. Same idea, sign in with your account instead of managing keys and separate billing.
And if you want access to a whole bunch of models from one place, the folks who make Hermes have their own service for exactly that. Run and it handles the model provider and tool gateway together in one OAuth flow through Nous Portal. It's the easiest path if you'd rather not think about which provider you're on.
Whichever route you pick, once the wizard finishes, run one more time and send a message to confirm the model actually responds. Good to go? Now let's get it on the network.

Web Tools
One thing worth knowing up front: out of the box, Hermes isn't great at looking things up on the web. It can reason all day, but for real research you'll want to give it some proper tools. We're not setting this up in this guide, I just want you to know it's possible and show you what a solid stack looks like.
Here's the setup I personally run for web research:
- Brave Search finds relevant websites, projects, and sources.
- Browser Use Cloud navigates dynamic sites like Reddit using a persistent login and a residential proxy.
- Firecrawl extracts clean content from project websites, documentation, and changelogs.
- GitHub API verifies repository activity, releases, stars, and licensing.
- Hermes combines the data, filters the noise, analyzes trends, and sends reports off to Slack.
So the flow goes: Brave finds it, Browser Use navigates it, Firecrawl extracts it, GitHub verifies it, and Hermes analyzes it. Pretty slick once it's all wired together.
Here's the nice part. Once you've got a model established in Hermes like we just did, setting up stuff like this gets a whole lot easier, because you can literally ask Hermes to help and it'll walk you through the whole thing. That's kind of the whole point of having an agent.
Chat Integrations
Same deal as the web tools, we're not wiring this up today, but it's worth knowing about. Hermes can hook into chat platforms like Telegram and Slack, and honestly this is where a self-hosted agent starts to feel really nice to live with.
A few things it's great for:
- Quick chats on the go. Fire off a message from your phone without opening a dashboard or SSHing in. Sometimes you just want to ask it something real quick.
- Notifications. Have Hermes ping you in Slack or Telegram when a job finishes, something breaks, or one of those web research reports is ready.
- Automations. Kick off tasks on a schedule or a trigger and let the results land right in your chat.
To get started, the Messaging Platforms docs are the place to go. Hermes actually supports 20+ channels beyond just Telegram and Slack, Discord, WhatsApp, Signal, and so on, so pick whatever you already live in. And just like with the web tools, once you've got a model running you can ask Hermes to help you set any of this up. Get the core installation solid first, then bolt on the extras that fit how you actually work.
Installing NetBird on the Server
Now let's get the server onto your NetBird network. If you don't have a NetBird account yet, sign up for free real quick. The easiest install path is the official script:
For a server you're sitting at, opens a browser for SSO. For a headless machine, create a one-off setup key in the dashboard under Settings > Setup Keys and enroll with it. Check out the setup keys documentation for the details. Do note that setup keys are secrets, so keep them out of shell history, scripts, and Git.
Verify the peer is up and grab its NetBird IP:
You'll get an address from the CGNAT range, something like . Write that down, we'll use it a lot.
Groups and Access Policies
Don't treat every peer on your network as equally trusted. In the NetBird dashboard, create two groups:
- for the server running Hermes
- for the laptops, phones, or users allowed to operate it
Then create a one-way policy: source , destination , TCP, ports . That's it. Return traffic is handled automatically by NetBird's stateful firewall, so you don't need a reverse rule.

One gotcha that catches a lot of people: new NetBird accounts ship with a permissive Default policy allowing all peers to talk to all peers. NetBird policies are additive allow rules, so your nice narrow Hermes policy does nothing while that default is still enabled. Build and test your replacement policies first, then remove the default. The access control docs cover this well.
Setting Up the Official Dashboard
The Hermes dashboard refuses to start on a non-loopback address without authentication configured, which is exactly the behavior you want. The simplest provider for a VPN-only setup is the username and password one. Heads up, it's called but it is not HTTP Basic Auth. It's a login page with a session cookie, so testing with will just get you a 401 and some confusion.
We'll generate a password hash and signing secret without ever leaving your password in shell history. Let's take it one step at a time.
First, move into the Hermes directory and read your chosen dashboard password into a variable. The flag hides your typing, so nothing sensitive lands in your terminal or history. Do note that these two lines need to be run on their own, not pasted together with anything else. The command waits silently for your input (the terminal will look frozen, that's normal), and if you paste more lines along with it, it will swallow the next line as your password:
Type your password and press Enter. Next, hash that password with Hermes' own hashing helper and store the result. We pass the password through an environment variable so it never appears on the command line:
With the hash in hand, we no longer need the plaintext password, so clears it right away. Now generate a random signing secret for session cookies:
Finally, write all three values into your file. The and make sure only your user can read the file, and we the variables once they're saved:
The signing secret keeps sessions valid across restarts, so set it once and leave it alone. Next, install the dashboard dependencies (the embedded Chat tab needs the PTY helper too):
If pip comes back missing from the venv, run real quick and then the install command again.
Now create a systemd user service so the dashboard survives reboots and SSH logouts:
Here's the step everyone forgets: a systemd user service normally only runs while you're logged in. Enable lingering so it starts at boot and keeps running after you disconnect:
Check that it's alive and the auth gate is up:
You want to see with in the providers list. Now from any peer in your group, open , sign in, open Chat, and send a message. And just like that, you have a private AI agent dashboard from anywhere.
Do note that if you use Hermes Desktop, its remote backend is a separate headless process, not this dashboard. You can run one the same way with a second systemd unit on its own port, then point Desktop at it under Settings > Gateway > Remote gateway.
Adding the Community WebUI
The official dashboard is great for administration, but for daily chatting, especially on a phone, the community WebUI is the nicer experience. Install from a release instead of tracking master, this thing runs with real permissions on an always-on box. This grabs the latest stable release automatically:
You can always see what's current on the releases page , and if you'd rather pin a specific version, just set to that tag instead. Now let's set up its state directory and environment file, again one step at a time.
First, create the state directory (locked to your user), find your NetBird IP so the WebUI binds only to the mesh, and generate a random login password. We print the password once so you can save it:
Copy that password into your password manager right now, since this is the only time it's shown. Next, write the environment file with locked-down permissions, then clear the password variable once it's saved:
Finally, create the default workspace directory the WebUI will open into:
Notice we're binding directly to the NetBird IP here, so the WebUI never even listens on your LAN interface. It's a nice trick for services that only ever need to be reached over the mesh.
The service unit looks pretty familiar at this point:
Open from an allowed peer, log in, and start a chat. If streaming works and workspace browsing stays inside , you're good to go.
Do note that you may see a "Gateway not configured" message in the UI. That's normal and safe to ignore. It refers to Hermes' optional messaging gateway (Slack, Telegram, Discord, scheduled delivery), which we haven't set up. Browser chat doesn't use it, the WebUI runs Hermes in-process. If you want those messaging integrations later, followed by will get you there.
If raw NetBird IPs over HTTP inside your encrypted mesh are fine for you, you can honestly stop here. Everything already works.
Friendly HTTPS Names with NetBird Reverse Proxy
Typing IP addresses and ports gets old, so let's finish this properly. NetBird Reverse Proxy terminates TLS, gives your services real hostnames, and forwards traffic through the mesh to your peer. With NetBird-Only Access , only peers or users in the groups you choose can even reach the service.
Do note that Reverse Proxy is currently a beta feature, and NetBird-Only Access requires a proxy cluster that advertises the Private capability. The shared NetBird Cloud clusters don't currently offer that, so you'll want a Bring Your Own Proxy cluster, which the dashboard walks you through under Reverse Proxy > Clusters. Self-hosted NetBird users should follow the migration guide instead.
Once your cluster is online and shows the Private badge, create two HTTP services under Reverse Proxy > Services. For the dashboard:
| Field | Value |
|---|---|
| Mode | HTTP |
| Domain | |
| Target peer | your Hermes server |
| Target protocol | HTTP |
| Target port | |
| Pass Host Header | Enabled |
| Authentication | NetBird-Only Access |
| Access groups |

Under the Authentication tab, pick NetBird-Only Access and select your access groups:

Then a second one for the WebUI on port at . Use dedicated hostnames rather than paths, it saves you from a whole family of PWA, cookie, and WebSocket routing headaches. The target protocol is HTTP in both cases because NetBird handles the TLS and the backends speak plain HTTP internally.
Two backend tweaks make the apps proxy-aware. First, the dashboard's , which sets the OAuth callback URL. If you're only using the basic login you can skip this one, but set it if you use Nous Portal or another OAuth provider for dashboard sign-in:
And add these to , then restart that service:
If your access policies are locked down tight, remember the proxy's embedded peer also needs a path to the server. Put it in its own group (something like ) and add a one-way policy to on ports . If a service sits in , this policy is the first thing to check.
Now open both hostnames from an allowed peer. You should pass the NetBird-Only check, land on each app's login page, and sign in over proper HTTPS. Beautiful.
The Final Test
A setup isn't proven until it survives a reboot, so let's find out:
When the server comes back, check everything came up on its own:
Then run one chat through each hostname, and try connecting from a peer that's not in . That last test matters just as much as the happy path. A denial there proves your policies are actually doing something.
With all that, you now have an always-on AI agent with two web interfaces, reachable from anywhere, with nothing exposed to the public internet and three layers of access control between the world and an agent that can run commands on your server. Pretty simple when you break it down.
Need help? Refer to these official guides:
