Skip to content

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

dd if=/dev/zero of=bios-update.img bs=1M count=64
parted bios-update.img --script mklabel msdos
parted bios-update.img --script mkpart primary fat32 1MiB 100%
parted bios-update.img --script set 1 boot on

Format the partition as FAT32

Using mtools to format the partition inside the image directly, no loop device or root needed:

mformat -i bios-update.img@@1048576 -F -v WINPE ::

The @@1048576 offset (1 MiB) matches the partition start set by parted.

Copy the BIOS files

mcopy -i bios-update.img@@1048576 -s /path/to/M1V307_12G_ROM/* ::

Expected file structure on the USB:

/
โ”œโ”€โ”€ AfuEfix64.efi
โ”œโ”€โ”€ EFI/
โ”‚   โ””โ”€โ”€ BOOT/
โ”‚       โ”œโ”€โ”€ BOOTX64.efi
โ”‚       โ”œโ”€โ”€ Startup.nsh
โ”‚       โ””โ”€โ”€ bootia32.efi
โ”œโ”€โ”€ Flash.nsh
โ””โ”€โ”€ M1V307.rom

Write the image to a USB drive

sudo dd if=bios-update.img of=/dev/sdX bs=4M status=progress # (1)!
  1. Replace /dev/sdX with 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:

AfuEfix64.efi M1V307.rom /p /b /n /x /r /reboot

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:root dataset)
  • Hosts restricted to the Incus host's IP
  • ZFS lz4 compression 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:

Run the standalone borg binary
TMPDIR=/mnt/nvme0/tools/borg/tmp /mnt/nvme0/tools/borg/borg --version

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:

Authorized key entry for root
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.