TrueNAS¶
Hardware¶
The NAS runs on a Beelink ME Mini 6-Slot Home Storage NAS PC:
| Spec | Detail |
|---|---|
| CPU | Intel N95 / N150 |
| RAM | 12 GB (onboard) |
| Storage | 6 SATA bays |
BIOS Update¶
Beelink provides BIOS updates as a set of EFI shell files. The process involves creating a bootable FAT32 USB drive that auto-flashes the BIOS via the AMI EFI flash utility (AfuEfix64.efi).
Creating a bootable USB image¶
Rather than formatting a USB stick manually, create a raw .img file that can be written with dd to any USB drive:
Create a blank image with a FAT32 partition
Format the partition as FAT32
Using mtools to format the partition inside the image directly, no loop device or root needed:
The @@1048576 offset (1 MiB) matches the partition start set by parted.
Copy the BIOS files
Expected file structure on the USB:
Write the image to a USB drive
- Replace
/dev/sdXwith your USB drive.
Flashing the BIOS¶
Boot from USB
Restart the Beelink and press F7 repeatedly at the boot logo to open the boot menu. Select the USB drive.
Let the EFI shell flash automatically
The USB contains a Startup.nsh script that automatically locates and runs Flash.nsh, which executes:
The system will flash the BIOS and reboot automatically.
Warning
Do not power off the machine during the flash process. Ensure the power supply is reliable before starting.
Incus Backup Target¶
The NAS is the landing zone for the nightly Incus backups: the Incus host writes its instance and volume exports to an NFS share, then triggers Borg here so the tars are read at local NVMe speed instead of crossing the network a second time.
NFS share¶
The nvme0/backups-incus dataset is exported over NFS to the Incus host:
- Mapall User/Group set so client writes land as the dataset owner (the default root squash blocked writes on a
755 root:rootdataset) - Hosts restricted to the Incus host's IP
- ZFS
lz4compression makes the zero padding inside raw block volume exports nearly free on disk
Borg on the immutable OS¶
TrueNAS SCALE's root filesystem is read only and apt is disabled, but nothing on a data pool is. Borg runs from a self-contained standalone binary that never touches the OS:
/mnt/nvme0/tools/borg/
โโโ borg # standalone binary from GitHub releases
โโโ borg-backup.sh # create + prune + compact + monthly check
โโโ tmp/ # TMPDIR (see below)
โโโ .borg/ # ssh key, passphrase, known_hosts, config, cache
/tmp is noexec
The standalone Borg binary self-extracts at runtime and fails with failed to map segment because TrueNAS mounts /tmp with noexec. Point TMPDIR at a directory on the pool before running it:
borg-backup.sh exports TMPDIR, BORG_CONFIG_DIR, BORG_CACHE_DIR, BORG_PASSCOMMAND and BORG_RSH so that every path Borg uses lives on the pool, then runs borg create (with --lock-wait), borg prune and borg compact against the BorgBase repository. A freshness guard refuses to archive tars older than 12 hours, so a night with missing exports fails loudly instead of snapshotting stale data.
SSH trigger¶
The Incus host starts the NAS side with a dedicated SSH key that can only run the backup script:
command="/mnt/nvme0/tools/borg/borg-backup.sh",restrict ssh-ed25519 AAAA... incus-borg-trigger
The key is stored in the TrueNAS config database (Credentials โ Users โ root โ Authorized Keys), not hand-edited into /root/.ssh/authorized_keys. The middleware regenerates that file from the database on boot and after updates, so a UI-registered key survives upgrades and is included in config exports.
Full pipeline documentation and restore procedures: Backup & Restore.