<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jimmy Huang</title>
    <description>The latest articles on DEV Community by Jimmy Huang (@jimmy_huang_ad36387a6ce66).</description>
    <link>https://dev.to/jimmy_huang_ad36387a6ce66</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4039237%2F017e6d2a-6cce-45f8-b002-c4c98240c32d.png</url>
      <title>DEV Community: Jimmy Huang</title>
      <link>https://dev.to/jimmy_huang_ad36387a6ce66</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jimmy_huang_ad36387a6ce66"/>
    <language>en</language>
    <item>
      <title>How I run an AI agent 24/7 on a Raspberry Pi (and don't lose its memory)</title>
      <dc:creator>Jimmy Huang</dc:creator>
      <pubDate>Tue, 21 Jul 2026 06:00:59 +0000</pubDate>
      <link>https://dev.to/jimmy_huang_ad36387a6ce66/how-i-run-an-ai-agent-247-on-a-raspberry-pi-and-dont-lose-its-memory-28cn</link>
      <guid>https://dev.to/jimmy_huang_ad36387a6ce66/how-i-run-an-ai-agent-247-on-a-raspberry-pi-and-dont-lose-its-memory-28cn</guid>
      <description>&lt;p&gt;AI agents finally got useful this year. The problem I kept hitting wasn't the agent — it was that it had nowhere good to &lt;em&gt;live&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;My laptop sleeps. A cheap VPS gets its datacenter IP blocked by half the web. And I didn't love the idea of an agent with a real browser session running on my daily-driver machine, next to everything else.&lt;/p&gt;

&lt;p&gt;So I gave it its own computer: a Raspberry Pi that runs &lt;a href="https://github.com/NousResearch/hermes-agent" rel="noopener noreferrer"&gt;Hermes&lt;/a&gt; around the clock. This post is how I set it up — focused on the two things that actually separate a demo from something you rely on: &lt;strong&gt;staying up&lt;/strong&gt;, and &lt;strong&gt;not losing state&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnqv9ctswf99xp30lidgt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnqv9ctswf99xp30lidgt.png" alt="A small dedicated computer running an AI agent around the clock" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Everything here works on any always-on Linux box (a mini PC, a NUC, an old laptop). I'll use a Pi 5 as the concrete example.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why dedicated, always-on hardware?
&lt;/h2&gt;

&lt;p&gt;You &lt;em&gt;can&lt;/em&gt; run an agent on your laptop or a $5 VPS. Both work. A dedicated box earns its place when you hit one of these:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Your laptop sleeps.&lt;/strong&gt; An agent that checks something twice a day or replies to your inbox needs to be awake at 3am. A closed lid isn't.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Datacenter IPs get blocked.&lt;/strong&gt; Lots of the web (retail, banking, social, ticketing) treats VPS IP ranges as bots. A machine on your home connection browses from a residential IP, signed in where you're signed in.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You want the memory local.&lt;/strong&gt; Hermes keeps what it learns about you as files. On your own box, those files stay on your desk — you can read, edit, back them up, and move them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Honest tradeoff: a small box is &lt;strong&gt;not&lt;/strong&gt; a workstation. It's sized for an agent that works in short bursts around the clock, not for heavy parallel jobs or local model inference. That's the right tradeoff for this job — just know it going in.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Baseline
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A Raspberry Pi 5 (8 GB) with an NVMe SSD, running 64-bit Raspberry Pi OS (or any Debian/Ubuntu box).&lt;/li&gt;
&lt;li&gt;Install Hermes with the official installer:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://hermes-agent.nousresearch.com/install.sh | bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Run &lt;code&gt;hermes setup&lt;/code&gt; once and pick your model provider with &lt;code&gt;hermes model&lt;/code&gt; — a ChatGPT plan, OpenRouter, any API key, or a local endpoint. Hermes is provider-agnostic, so nothing here assumes one.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On Linux/macOS, Hermes stores its state in &lt;code&gt;~/.hermes&lt;/code&gt; by default (memory, skills, config, chat history). I'll refer to it as &lt;code&gt;$HERMES_HOME&lt;/code&gt; — confirm the path on your own install:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;HERMES_HOME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/.hermes"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Keep it running with systemd
&lt;/h2&gt;

&lt;p&gt;Run the agent as a service so it starts on boot and restarts if it dies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="c"&gt;# /etc/systemd/system/hermes.service
&lt;/span&gt;&lt;span class="nn"&gt;[Unit]&lt;/span&gt;
&lt;span class="py"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Hermes Agent&lt;/span&gt;
&lt;span class="py"&gt;After&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;network-online.target&lt;/span&gt;
&lt;span class="py"&gt;Wants&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;network-online.target&lt;/span&gt;
&lt;span class="c"&gt;# don't hammer restarts if it's crash-looping
&lt;/span&gt;&lt;span class="py"&gt;StartLimitIntervalSec&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;300&lt;/span&gt;
&lt;span class="py"&gt;StartLimitBurst&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;5&lt;/span&gt;

&lt;span class="nn"&gt;[Service]&lt;/span&gt;
&lt;span class="py"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;simple&lt;/span&gt;
&lt;span class="py"&gt;User&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;hermes&lt;/span&gt;
&lt;span class="py"&gt;WorkingDirectory&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;%h&lt;/span&gt;
&lt;span class="c"&gt;# `hermes gateway` runs the always-on messaging gateway (Telegram, Discord, etc.)
# Use the absolute path from `which hermes` — usually ~/.local/bin.
&lt;/span&gt;&lt;span class="py"&gt;ExecStart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;%h/.local/bin/hermes gateway&lt;/span&gt;
&lt;span class="py"&gt;Restart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;always&lt;/span&gt;
&lt;span class="py"&gt;RestartSec&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;5&lt;/span&gt;

&lt;span class="nn"&gt;[Install]&lt;/span&gt;
&lt;span class="py"&gt;WantedBy&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;multi-user.target&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl daemon-reload
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable&lt;/span&gt; &lt;span class="nt"&gt;--now&lt;/span&gt; hermes
systemctl status hermes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Survive a power cut
&lt;/h2&gt;

&lt;p&gt;An always-on box will lose power eventually. Make sure it comes back on its own:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Auto power-on after an outage.&lt;/strong&gt; On a mini PC/NUC, enable "Restore on AC Power Loss" in the BIOS. A Pi powers on whenever it gets power, so just make sure its circuit does.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Boot straight into the service.&lt;/strong&gt; &lt;code&gt;systemctl enable&lt;/code&gt; (above) already handles that.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Optional hardware watchdog.&lt;/strong&gt; Many boards (the Pi included) expose a watchdog that reboots the machine if the kernel hangs — enable it via &lt;code&gt;RuntimeWatchdogSec&lt;/code&gt; in &lt;code&gt;/etc/systemd/system.conf&lt;/code&gt; for an extra safety net.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use a journaling filesystem&lt;/strong&gt; (ext4/xfs — the default) so an abrupt power loss doesn't corrupt state mid-write.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8e5v74kmkolx2y7gq93s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8e5v74kmkolx2y7gq93s.png" alt="The always-on box running headless after a reboot" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Back up what matters
&lt;/h2&gt;

&lt;p&gt;The whole point of a persistent agent is that it &lt;em&gt;remembers&lt;/em&gt;. So the thing you must protect is &lt;code&gt;$HERMES_HOME&lt;/code&gt;. Back it up &lt;strong&gt;encrypted&lt;/strong&gt;, on a schedule, and &lt;strong&gt;off the box&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I use &lt;a href="https://github.com/FiloSottile/age" rel="noopener noreferrer"&gt;&lt;code&gt;age&lt;/code&gt;&lt;/a&gt; for encryption because it's tiny and transparent, and I packaged the whole backup/verify/restore/health flow into a small open skill so I'm not copy-pasting shell each time: &lt;strong&gt;&lt;a href="https://github.com/JimmyHuang2002/hermes-backup-recovery" rel="noopener noreferrer"&gt;hermes-backup-recovery&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The core of &lt;code&gt;backup.sh&lt;/code&gt; is just:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# encrypted, timestamped, with retention&lt;/span&gt;
&lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-C&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HERMES_HOME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-czf&lt;/span&gt; - &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;basename&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HERMES_HOME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | age &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$AGE_RECIPIENT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$BACKUP_DIR&lt;/span&gt;&lt;span class="s2"&gt;/hermes-&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; +%Y%m%dT%H%M%SZ&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;.tar.gz.age"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it nightly with a systemd timer, and &lt;strong&gt;keep at least one copy off the box&lt;/strong&gt; — another machine, a NAS, or object storage. A backup that lives on the same SSD as the original doesn't survive that SSD dying. Keep the &lt;code&gt;age&lt;/code&gt; &lt;strong&gt;private&lt;/strong&gt; key off the box too, or a thief/failure takes both.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1ht2f8fa19p46pv4e30d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1ht2f8fa19p46pv4e30d.png" alt="Encrypted backups of the agent's state stored off the device" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Prove you can restore
&lt;/h2&gt;

&lt;p&gt;A backup you've never restored is a guess, not a backup. Once a month I run a restore drill into a scratch directory and confirm the files come back:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# from the hermes-backup-recovery skill&lt;/span&gt;
&lt;span class="nv"&gt;BACKUP_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/backups &lt;span class="nv"&gt;AGE_IDENTITY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;~/age/keys.txt ./scripts/verify.sh
&lt;span class="nv"&gt;RESTORE_TARGET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/tmp/restore-drill ./scripts/restore.sh &lt;span class="nt"&gt;--apply&lt;/span&gt; /backups/hermes-&amp;lt;timestamp&amp;gt;.tar.gz.age
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;verify.sh&lt;/code&gt; checks the checksum and test-decrypts the archive; &lt;code&gt;restore.sh&lt;/code&gt; is dry-run by default and only writes with &lt;code&gt;--apply&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Health checks
&lt;/h2&gt;

&lt;p&gt;Catch problems before they're outages. A one-shot check (service up, disk not full, last backup recent) on a timer, exiting non-zero when something needs attention, wired to whatever notification you already read:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./scripts/healthcheck.sh   &lt;span class="c"&gt;# exit 0 = healthy, 1 = look at me&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  7. Security basics
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Keep it on your LAN.&lt;/strong&gt; You don't need to expose the agent to the public internet. Reach it over your home network or a private tunnel (Tailscale/WireGuard). Don't port-forward it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secrets in root-only files&lt;/strong&gt; (&lt;code&gt;chmod 600&lt;/code&gt;), never committed anywhere.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Run it as its own unix user&lt;/strong&gt;, not root, so a bad browsing session can't touch the rest of the box.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgg48u1zmjl2aijgpzn7f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgg48u1zmjl2aijgpzn7f.png" alt="The agent kept on a private LAN rather than exposed to the internet" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;Two repos with everything above, both MIT:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;📘 &lt;a href="https://github.com/JimmyHuang2002/hermes-always-on" rel="noopener noreferrer"&gt;&lt;strong&gt;hermes-always-on&lt;/strong&gt;&lt;/a&gt; — the full 24/7 guide, systemd units, and timers&lt;/li&gt;
&lt;li&gt;🛠 &lt;a href="https://github.com/JimmyHuang2002/hermes-backup-recovery" rel="noopener noreferrer"&gt;&lt;strong&gt;hermes-backup-recovery&lt;/strong&gt;&lt;/a&gt; — the backup/verify/restore/health skill (&lt;a href="https://agentskills.io" rel="noopener noreferrer"&gt;agentskills.io&lt;/a&gt; format)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I got tired of wiring this up by hand on every box, so I ended up packaging it into a small always-on computer that ships with Hermes, the dashboard, and backups/recovery preconfigured — &lt;a href="https://mangot.art/?utm_source=devto" rel="noopener noreferrer"&gt;MangoTart&lt;/a&gt;. That part's optional; the guides above stand on their own on hardware you already have.&lt;/p&gt;

&lt;p&gt;If you're running an agent 24/7 on something, I'd love to hear what board and what breaks for you — that's half the reason I wrote this down.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>raspberrypi</category>
      <category>selfhosted</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
