Skip to content

WongTek Blog

A Technology Blog and Stuff

Menu
  • Welcome
  • Posts
  • About Us
  • Contact US
Menu
Banner: self-hosted AI agent on a home KVM lab

Running a Self-Hosted AI Agent (Hermes) on a Home KVM Lab

Posted on September 21, 2026

I run Hermes as a self-hosted AI agent in a small KVM lab rather than installing it directly on my workstation. The VM is configured as a QEMU q35 guest with 8 GiB assigned memory. Inside the guest, Hermes and its supporting services run as a regular user under systemd --user.

This setup gives me a clean boundary for the agent, predictable restarts, and a simple way to inspect what is actually running. The important detail is to point systemd directly at the virtual environment entry point instead of using a shell wrapper.

1. Confirm that the host is a virtual machine

From inside the guest, I first confirm that I am troubleshooting the VM I expect:

systemd-detect-virt --vm
uname -srmo

On this VM the result is kvm, followed by the Linux kernel and architecture. This only proves the guest virtualization type; it does not show the complete QEMU device configuration. I check the VM definition from the KVM host when I need to verify the machine type, assigned RAM, disks, or devices.

2. Check the memory the guest really sees

Guest-visible memory is the number that matters for sizing services:

free -h
awk '/MemTotal|SwapTotal/ {print}' /proc/meminfo

My guest currently reports about 7.2 GiB of RAM and a 3.1 GiB swap device. The configured VM size is 8 GiB, but the guest will always report less than the assigned amount because firmware and virtual devices consume some memory.

This check also catches a common KVM surprise: a virtio-balloon device can make the guest see less memory than expected. If memory appears to be missing, check the QEMU/libvirt definition for a balloon device and compare it with MemTotal inside the guest before tuning applications.

3. Run the agent from its virtual environment

I keep Hermes in its own virtual environment:

ls -l /home/hermes/.hermes/hermes-agent/venv/bin/hermes
head -n 3 /home/hermes/.hermes/hermes-agent/venv/bin/hermes

The systemd unit should call that entry point directly. My dashboard unit uses:

ExecStart=/home/hermes/.hermes/hermes-agent/venv/bin/hermes dashboard --port 9119 --host 0.0.0.0 --no-open

I avoid pointing a systemd unit at ~/.local/bin/hermes. That file is a shell wrapper intended for an interactive shell and can have a different environment from the user service manager. Calling the venv entry point makes the interpreter and installed dependencies unambiguous.

4. Use systemd user services for the long-running components

For a single-user lab, systemd --user is enough. I can see the running services with:

systemctl --user --no-pager --type=service --state=running

The core services in my setup are the Hermes dashboard, the Hermes gateway, the Hermes web UI, and the Hindsight daemon and UI. To inspect the exact command and unit file for each service:

for u in hermes-dashboard.service hermes-gateway.service hermes-webui.service hindsight-daemon.service hindsight-ui.service; do
  systemctl --user show "$u" \
    -p Id -p ActiveState -p SubState -p ExecStart -p FragmentPath \
    --no-pager
done

I want each service to show ActiveState=active and SubState=running. The FragmentPath output tells me which unit file is actually being used, while ExecStart removes ambiguity about which Python or Hermes executable launched the process.

5. Verify the processes, not just the unit state

A running systemd unit does not automatically mean the expected application is running. I also inspect the process command lines:

ps -eo pid,user,comm,args --no-headers \
  | grep -E '(hermes|hindsight)' \
  | grep -v grep

In my environment this shows the Hermes gateway running with the venv Python module, the dashboard using the venv Hermes executable, and the Hindsight processes using their intended environment. This is a useful check after an upgrade because a service can remain active while launching an old path or an unexpected interpreter.

6. Troubleshoot the user manager before restarting applications

When a user service does not start, I check the unit state and recent journal together:

systemctl --user status hermes-dashboard.service --no-pager
journalctl --user -u hermes-dashboard.service -n 100 --no-pager

If the service is enabled for boot but does not start after the VM reboots, I check whether the user manager is enabled to remain available without an interactive login:

loginctl show-user hermes -p Linger
loginctl show-user hermes -p State

I only change linger deliberately, because it controls whether the user service manager survives logout:

sudo loginctl enable-linger hermes

After changing a unit file, I reload the user manager and restart only the affected service:

systemctl --user daemon-reload
systemctl --user restart hermes-dashboard.service
systemctl --user status hermes-dashboard.service --no-pager

7. Keep the VM boundary simple

The useful pattern here is deliberately boring: a dedicated KVM guest, a dedicated Python environment, and user-level systemd units with explicit paths. I can verify the guest resources, the unit definitions, and the actual processes without depending on an interactive shell.

That makes upgrades easier to reason about. If something breaks, I can identify whether the problem is the VM resource definition, the Python environment, the systemd unit, or the application itself instead of treating “the agent is down” as one undifferentiated failure.

Like this:

Like Loading…

Related

Leave a ReplyCancel reply

Recent Posts

  • Running a Self-Hosted AI Agent (Hermes) on a Home KVM Lab
  • Reviving a 12-Year-Old WordPress Blog: A Migration Audit
  • Fixing USB 3.0 Problems with some VIA/NEC controller cards on Windows 7/8/8.1
  • Controlling the GPU switching of your new Retina MacBook Pro
  • Setting up a Linux disk quota for users

Recent Comments

  • Peter Wong on Custom firmware for Asus RT-AC66U
  • Peter Wong on Custom firmware for Asus RT-AC66U
  • Michael Hasse on Custom firmware for Asus RT-AC66U
  • Peter Wong on AirPrint and Ubuntu
  • Peter Wong on AirPrint and Ubuntu

Archives

  • September 2026
  • March 2014
  • November 2013
  • October 2013
  • April 2013
  • November 2012
  • October 2012
  • June 2012
  • April 2012
  • March 2012
  • February 2012
  • January 2012
  • December 2011
  • November 2011
  • September 2011
  • July 2011
  • May 2011
  • April 2011
  • February 2011
  • October 2010

Categories

  • Cars
  • Family
  • Food
  • General
  • How Tos
  • iOS
  • iPhone
  • Linux
  • Mac
  • Network
  • Reviews
  • Technology
  • Uncategorized
  • Windows

Meta

  • Log in
  • Entries feed
  • Comments feed
  • WordPress.org

Archives

  • September 2026
  • March 2014
  • November 2013
  • October 2013
  • April 2013
  • November 2012
  • October 2012
  • June 2012
  • April 2012
  • March 2012
  • February 2012
  • January 2012
  • December 2011
  • November 2011
  • September 2011
  • July 2011
  • May 2011
  • April 2011
  • February 2011
  • October 2010

Categories

  • Cars
  • Family
  • Food
  • General
  • How Tos
  • iOS
  • iPhone
  • Linux
  • Mac
  • Network
  • Reviews
  • Technology
  • Uncategorized
  • Windows
©2026 WongTek Blog | Design: Newspaperly WordPress Theme
%d