Your Pi-Hosted AI Agent Has a Write Budget. Spend It on Purpose.
Most self-hosted agent failures are loud. A token expires, a cron job errors, a webhook returns a 500. You see it, you fix it.
Storage wear is different. It is slow, quiet, and then suddenly total. One morning the Pi boots read-only, your agent can’t write its own state, and every job that “worked yesterday” fails in a new and confusing way.
If your agent runs on a Raspberry Pi booting from an SD card, you have a write budget whether you have named it or not. This post is about naming it and spending it on purpose.
Why agents are unusually hard on flash
A typical web server writes a modest access log and little else. An agent host is different. In a normal week it might:
- Append to a log line for every tool call, model call, and heartbeat.
- Rewrite a session transcript after every turn.
- Checkpoint memory or a vector index.
- Download pages, images, and audio to a cache it forgets to clean.
- Run package updates, build steps, and git pulls.
- Write a SQLite journal on every small state change.
None of these is dramatic alone. Together they are thousands of small writes per hour, around the clock. Consumer SD cards have limited wear leveling and no meaningful health reporting. They fail suddenly, often by corrupting the filesystem rather than by cleanly refusing writes.
Step 1: measure before you move anything
Don’t guess which process is the offender. Measure it.
Check lifetime writes to the root device:
cat /sys/block/mmcblk0/stat
The seventh field is sectors written. Multiply by 512 for bytes. Record it, wait a day, record it again. The difference is your daily write volume.
Then find who is writing:
sudo iotop -oPa
Let it run for a few minutes during a busy period. Accumulated totals per process tell you far more than a single snapshot. Also check journalctl --disk-usage; a chatty service can quietly fill the journal and rotate it constantly, which is write amplification you didn’t ask for.
Write the baseline down. A number you can compare next month is worth more than a feeling.
Step 2: sort writes into three buckets
Every write your agent makes falls into one of three buckets.
Disposable. Caches, temp downloads, scratch files, build output. If you lose it, nothing breaks; it regenerates. These belong in RAM (tmpfs) or on a drive you don’t mind replacing.
Reconstructable. Logs, metrics, rendered reports, indexes. Losing them hurts a little, but you can rebuild or live without them. These should be rate-limited, rotated, and ideally kept off the boot card.
Precious. Config, credentials, memory files, transcripts you actually reread, the git repos holding your work. These must survive. They deserve the most reliable storage you own, plus a backup.
Most Pi setups treat all three the same: write everything to the same card, forever. The fix is mostly about stopping that.
Step 3: move the hot paths
You don’t need a rebuild. A few targeted changes cut most of the wear.
Boot from an SSD. The Pi 5 handles USB or NVMe boot well, and a modest SSD has far better endurance and failure behavior than a card. If you do only one thing from this post, do this one. The card becomes a boot stub or a spare.
Put temp and cache in RAM. Mount /tmp and your agent’s scratch directory as tmpfs. Anything disposable stops touching flash at all. Cap the size so a runaway download can’t eat your memory.
Cap the journal. Set SystemMaxUse=200M in journald.conf and lower the log level on noisy services. Persistent logs are useful; unbounded verbose logs are a tax.
Batch your own writes. If your agent appends a line per event, buffer and flush every few seconds. If it rewrites a whole transcript each turn, switch to append-only. Small code changes here often beat any hardware change.
Turn off access-time updates. Mount with noatime so reads stop generating writes.
Step 4: set a threshold and an alarm
A budget without an alarm is a wish.
Pick a daily write ceiling based on your baseline. Something like “alert if the root device writes more than twice the weekly median in a day.” Add a tiny cron job that reads the stat counter, compares, and pings you when it spikes.
A spike almost always means a bug, not growth: a retry loop logging stack traces, a job that keeps re-downloading the same file, a cache with no expiry. The alarm turns a future dead card into a Tuesday afternoon fix.
Also alarm on free space and on the filesystem remounting read-only. Those are the last warning you get.
Step 5: assume the disk will die anyway
Even with all of the above, storage fails. Plan the day it does.
- Back up the precious bucket off the device. Not to the same SSD. Push config and memory to a private git repo or another machine on a schedule, and confirm a restore actually works by doing one.
- Keep a rebuild note. A short document listing the packages, services, and cron entries needed to bring a fresh Pi back to a working agent. Future you at 11 p.m. will be grateful.
- Make the agent fail gracefully. If a write fails, the agent should stop and say so, not loop, not silently drop state, and not pretend it succeeded. A job that reports “could not write checkpoint” is a gift compared to one that reports success and loses data.
What this buys you
None of this is glamorous. Nobody ever posted a screenshot of a well-tuned noatime mount.
But the point of self-hosting an agent is that it keeps working while you aren’t looking. Hardware wear is one of the few failure modes that is entirely predictable and entirely preventable with an afternoon of work.
Measure your writes. Sort them into disposable, reconstructable, and precious. Move the hot paths off the card. Set an alarm. Back up what matters.
Then spend your attention on the interesting problems, like what your agent should be doing, instead of whether it still has somewhere to write it down.
More from the build log
Suggested
Want the full MarketMai stack?
Get the core MarketMai guides and operator playbooks in one premium bundle for $49.
View Bundle