What Storing Model Files Does to an SBC's SD Card Over Time
9 min read · updated August 11, 2026
An SD card fails from writes, not from reads, and a machine running local models writes in three very different patterns. Only one of them is obvious, and it is not the one that kills the card.
Where the writes come from
Three sources, in ascending order of damage:
- Model downloads. Large, sequential, infrequent. A 4 GB pull is 4 GB written once, in big contiguous chunks, which is the pattern flash handles best. Annoying for space, close to harmless for endurance.
- Logs, metrics and databases. Small, frequent, scattered. A journald flush, an SQLite commit or a metrics append is a few kilobytes, and the card cannot write a few kilobytes — see the next section.
- Swap. Small, frequent, scattered, and unbounded. This is the one that ends cards. A model that does not fit in RAM pages weights in and out continuously for the whole of every generation, and the writes never stop while the workload runs.
The intuition to correct is that the multi-gigabyte model file is the problem. It is not. Downloading a 4 GB model once a week is 208 GB a year of the friendliest possible writes. A machine swapping steadily because the model is 1 GB too large can exceed that in a day, in the least friendly pattern there is.
Why one gigabyte written is not one gigabyte
NAND flash cannot overwrite in place. It is programmed in pages and erased in much larger blocks, so changing a few kilobytes means reading a whole block, modifying it, erasing it and writing it back. The ratio of what the flash actually programmes to what the operating system asked for is write amplification, and on a small controller with little spare area it can be large.
Three things make it worse specifically on an SD card in an SBC:
- Small random writes. A 4 KiB journal append can cause a full erase block to be rewritten. The amplification factor for that single write is the block size divided by 4 KiB.
- Limited wear levelling. Consumer cards implement simpler levelling than an SSD, so the blocks holding your filesystem journal, your swap file and your logs take a disproportionate share of the erase budget while the blocks holding the model file — written once, read forever — sit untouched.
- No TRIM in the usual path. Without discard, the controller does not know which blocks are free, so it has fewer clean blocks to work with and does more relocation to find one.
This is why the SD Association’s Application Performance Class exists at all — A2 requires 4,000 random-read IOPS, 2,000 random-write IOPS and 10 MB/s sustained sequential, along with command queueing and caching, because random small I/O is the workload cards historically handled worst. An A2 card handles that workload faster. It does not stop it consuming erase cycles.
It is also worth being clear about which claims on the packaging are and are not endurance claims. Speed class, UHS bus rating and application performance class all describe throughput and latency. None of them describes how long the card survives. The specifications that do are published as terabytes written, and they appear on industrial and high-endurance lines rather than on the card you get in a bundle — which is the practical reason to prefer a card whose vendor publishes a TBW figure at all, regardless of what the figure is.
The endurance budget, derived
Consumer cards use TLC NAND, which industry endurance guidance places somewhere in the hundreds to low thousands of program/erase cycles per block. Take a deliberately round, deliberately optimistic set of assumptions and see what comes out:
assume: 32 GB card
1,000 P/E cycles per block
perfect wear levelling (optimistic)
write amplification of 4x (optimistic for small random writes)
raw endurance = 32 GB x 1,000 = 32,000 GB written to NAND
host endurance = 32,000 GB / 4 = 8,000 GB from the OS's viewNow spend it. A steady 2 MB/s of swap traffic — modest for a machine paging model weights — is:
2 MB/s x 86,400 s = 172.8 GB/day 8,000 GB / 172.8 GB per day = ~46 days
Against the same budget, the model downloads look like this:
4 GB pull, weekly = 208 GB/year 8,000 GB / 208 GB per year = ~38 years
Every number above is an assumption, labelled, and the real ones will differ — wear levelling is not perfect, amplification on small random writes is usually worse than 4x, and cards vary by an order of magnitude. But the conclusion is robust to all of it, because the two answers differ by a factor of roughly three hundred. Swap is the entire problem. Model storage is not.
What actually fixes it
- Stop swapping. Run a model whose weights and context fit in RAM. If they do not, the fix is a smaller model or a smaller quantization — not a bigger swap file, which converts an out-of-memory error into a slow machine and a dying card.
- Use zram for the residual pressure. It compresses pages in RAM and writes nothing to storage, so it costs endurance nothing. It buys less headroom than a disk swap file, which is the correct trade here.
- Move the model store to a USB SSD. Set
OLLAMA_MODELS, or keep your GGUF directory there — the systemd override for this is three lines. The main win is not endurance, it is read speed on model load and taking the large sequential writes off the card entirely. - Move swap to the SSD too if you keep any, by pointing
CONF_SWAPFILEat it. An SSD has vastly more spare area, real wear levelling and published endurance. - Cap the logs. Set
Storage=volatileor a smallSystemMaxUse=in/etc/systemd/journald.conf. Small and constant is the pattern that amplifies worst. - Mount with reduced metadata writes.
noatimestops a read updating an inode timestamp, which otherwise turns reading your model file into a write.
Noticing before it fails
Cards do not usually announce themselves. They go read-only, or they start returning corrupted data on blocks that were fine yesterday, and the first symptom is often a filesystem that will not remount after a reboot. Watch the write volume rather than waiting for that:
# cumulative sectors written per device since boot (field 10 x 512 bytes)
awk '$3 ~ /mmcblk0$/ { print $3, $10 * 512 / 1e9 " GB written" }' /proc/diskstats
# is anything swapping right now
vmstat 5 5 # the si/so columns should be 0
# how much swap is in use at all
free -hNon-zero so in vmstat during generation is the signal that matters. It means the model does not fit, the card is being consumed to hide that, and no configuration change other than a smaller model will stop it. Keep a written image of your working card as well: the failure is not gradual from the user’s side, and restoring an image beats rebuilding a machine.