How SD Cards Fail
SD cards use NAND flash. Every cell has a limited number of program/erase (P/E) cycles — typically 3,000 for TLC NAND and 10,000-30,000 for MLC/pSLC. The card's internal controller spreads writes across cells (wear leveling), but there's a catch: write amplification.
When the OS writes a 4 KB block, the card may internally erase and rewrite a 4 MB erase block. A single 4 KB journal write can cause 1,000x amplification on a bad controller. Consumer SD cards — the ones optimized for sequential camera writes — have particularly poor random write handling.
On an always-on SBC, systemd journal, syslog, package manager databases, and tmpfiles can generate 1-10 GB of writes per day. At that rate, a consumer 32 GB card can wear out in 6-12 months.
Choosing Endurance Cards
For always-on SBC use, buy cards designed for continuous write workloads:
| Card | Endurance Rating | NAND Type | 32 GB Price (approx.) | Notes |
|---|---|---|---|---|
| Samsung PRO Endurance | 140 TBW (128 GB model) | pSLC-mode TLC | $12-15 | Best overall for SBC use |
| SanDisk MAX ENDURANCE | 120 TBW (128 GB model) | MLC | $13-16 | Solid alternative |
| Samsung EVO Select | Not rated for endurance | TLC | $8-10 | Fine for testing, not for production |
| Cheap no-name card | Unknown | QLC/TLC | $4-6 | Do not use for always-on systems |
Even better: move your root filesystem to SATA and use the SD card only for boot. See the SD boot + SATA root guide for the full procedure.
Reducing Writes
Mount /tmp and /var/tmp as tmpfs
Temporary files should live in RAM, not on flash:
# Add to /etc/fstab
tmpfs /tmp tmpfs defaults,noatime,nosuid,nodev,size=64M 0 0
tmpfs /var/tmp tmpfs defaults,noatime,nosuid,nodev,size=32M 0 0
# Apply immediately without reboot
sudo mount -a
# Verify
mount | grep tmpfs
Mount root with noatime
By default, Linux updates the "last accessed" timestamp on every file read. On a busy system this generates thousands of unnecessary writes per hour.
# In /etc/fstab, add noatime to your root mount:
/dev/mmcblk0p1 / ext4 defaults,noatime,commit=600 0 1
The commit=600 option increases the ext4 journal commit interval to 600 seconds (default is 5). This batches writes at the cost of potentially losing 10 minutes of data on sudden power loss.
log2ram: Keep Logs in RAM
log2ram is the single most effective write-reduction tool for SD-based SBCs. It mounts /var/log as a tmpfs and syncs to disk once daily (or on shutdown).
# Install log2ram
echo "deb [signed-by=/usr/share/keyrings/azlux-archive-keyring.gpg] http://packages.azlux.fr/debian/ bookworm main" | sudo tee /etc/apt/sources.list.d/azlux.list
sudo wget -O /usr/share/keyrings/azlux-archive-keyring.gpg https://azlux.fr/repo.gpg
sudo apt update
sudo apt install log2ram
# Configure log size (default 40M may be too small for verbose services)
sudo nano /etc/log2ram.conf
# Set SIZE=64M or SIZE=128M depending on your RAM
# Reboot to activate
sudo reboot
# Verify it's working
mount | grep log2ram
# Should show: log2ram on /var/log type tmpfs
Disable Swap on SD
Swap on an SD card is a fast path to card death. Each page-out is a random write to flash.
# Check if swap is active
swapon --show
# Disable swap immediately
sudo swapoff -a
# Prevent swap from being re-enabled on boot
sudo systemctl mask swap.target
# Remove any swap entries from /etc/fstab
sudo nano /etc/fstab
# Comment out or delete any swap lines
# Enable zram (Armbian does this by default)
sudo apt install zram-tools
# Or on Armbian: sudo armbian-config -> System -> Zram
Journal and Filesystem Settings
Reduce systemd journal writes
sudo nano /etc/systemd/journald.conf
# Add or modify:
[Journal]
Storage=volatile
Compress=yes
RuntimeMaxUse=32M
Storage=volatile keeps the journal in RAM only. Combined with log2ram, this eliminates almost all logging-related writes.
sudo systemctl restart systemd-journald
Reduce ext4 journal overhead
If your root is ext4 on the SD card:
# Check current journal size
sudo dumpe2fs /dev/mmcblk0p1 2>/dev/null | grep "Journal size"
# Optionally convert to writeback journaling (less safe, fewer writes)
sudo tune2fs -o journal_data_writeback /dev/mmcblk0p1
Monitoring Card Health
SD cards don't support SMART. Monitoring options are limited, but not zero:
# Check for media errors in dmesg
dmesg | grep -iE "mmc|mmcblk|error|fail|readonly"
# Check the card's internal health register (if supported)
# This requires mmc-utils
sudo apt install mmc-utils
sudo mmc extcsd read /dev/mmcblk0 | grep -i "life"
# Look for: Device life time estimation
# Values: 0x01 = 0-10% used, 0x0A = 90-100% used, 0x0B = exceeded
# Track total bytes written to the card
cat /sys/block/mmcblk0/stat
# Field 7 (sectors written) * 512 = total bytes written
awk '{print "Bytes written:", $7 * 512 / 1024 / 1024 / 1024, "GB"}' /sys/block/mmcblk0/stat
Detecting Imminent Failure
Warning signs that your SD card is about to fail:
- dmesg shows mmc errors:
dmesg | grep -i "mmc0: error" # Any "I/O error" or "Response CRC error" messages are bad - Filesystem goes read-only spontaneously:
mount | grep mmcblk0 # If you see "ro" where it should be "rw", the kernel remounted due to errors - Commands randomly fail with I/O errors
- fsck finds errors on every boot
- Write speeds drop dramatically — worn blocks take longer to program
When a Card Starts Failing
Don't try to rescue a failing card. Extract what you can and replace it.
# 1. Immediately image whatever you can read
sudo dd if=/dev/mmcblk0 of=/path/to/rescue-image.img bs=4M conv=noerror,sync status=progress
# conv=noerror skips unreadable blocks instead of aborting
# 2. Mount the image on another machine and copy out important files
sudo losetup --partscan --find --show rescue-image.img
sudo mount /dev/loop0p1 /mnt
cp -a /mnt/home /mnt/etc /mnt/var/lib /path/to/backup/
sudo umount /mnt
sudo losetup -d /dev/loop0
# 3. Flash a fresh image to a NEW endurance card
# See the Armbian flashing guide or Debian install guide for instructions