raspberry pi 4: nas and home server

Completed build

/img/projects-raspberrypi-completed.gif

Component list

  • Raspberry Pi 4 (4GB)
  • Raspberry Pi official USB-C power supply
  • 2x Gigabyte 1TB SSD
  • 2x StarTech.com USB 3.0 to SATA HDD/SSD Adapter Cable
  • TerraPi Alpha case: dual SSD (link)
  • 52Pi Low-Profile ICE Tower Cooling Fan

Build notes

The build is pretty simple, these notes are where I diverged from the instructions that came with each of the components.

Cooler + Alpha case compatibility

As can be seen in this official picture, the alpha pi case comes with a fan attachment for the top.

/img/alpha-pi-dual.jpeg

I wanted to use the ICE Tower cooler instead. This is possible as the ICE Tower cooler comes with copper standoffs that will screw (upside down) into the alpha pi case. The cooler can be mounted with the included screws into the upside-down standoffs.

/img/projects-raspberrypi-standoffs.jpeg

Setup

OS

I installed the 64bit version of Ubuntu server, this has good support for ZFS.

The OS is installed on partitions on one of the SSDs. This was done using the official Raspberry Pi Imager tool.

There following partitions exist;

Disk1

  • /dev/sda1: 256MB FAT 32 boot partition
  • /dev/sda2: 8GB ext4 OS partition
  • /dev/sda3: 950GB unformatted partition for ZFS

Disk2

  • /dev/sdb1: 950GB unformatted partition for ZFS
  • /dev/sdb2: 2GB linux swap partition

Overclock

The tower cooler is so effective that I overclocked the pi from 1500MHz to 2GHz with the following config;

  • /boot/firmware/config.txt
1
2
3
4
5
6
[pi4]
max_framebuffers=2

# overclock conig
arm_freq=2000
over_voltage=5

ZFS

The data partitions for zfs are not created with a filesystem.

The two disks are setup as a single mirrored vdev.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
$ zpool status
  pool: tank
 state: ONLINE
  scan: ...
config:

	NAME                                       STATE     READ WRITE CKSUM
	tank                                       ONLINE       0     0     0
	  mirror-0                                 ONLINE       0     0     0
	    scsi-SASMT_ASM105x_ABC123-part3        ONLINE       0     0     0
	    scsi-SASMT_ASM105x_XYZ456-part1        ONLINE       0     0     0

I created a parent-dataset at;

  • /tank/d1
1
2
3
4
$ zfs list
NAME                USED  AVAIL     REFER  MOUNTPOINT
tank               5.29G   909G       96K  /tank
tank/d1            5.29G   909G       96K  /tank/d1

No data should be written directly into the zpool, without using a dataset.

ZFS tuning

I set the following options for better performance.

Pool options

  • ashift=12: set the disk block size to 4k (standard for SSDs post 2011)
  • autoexpand=on: allow the disks to autoexpand if replaced

Dataset options

  • atime: off: don’t store the last accessed time
  • compression: lz4: compress data on disk
  • recordsize: 1M: most data will be media and images, better performance
  • xattr: sa: store attribute data in nodes, and not in filesystem

Snapshots

I use github.com/jimsalterjrs/sanoid to create automated and expiring snapshots on a schedule. I use the default schedule of;

  • 36x hourly snapshots
  • 30x daily snapshots
  • 12x monthly snapshots
  • 0x yearly snapshots

To restore the entire snapshot you can rollback, to access a specific file you can mount the snapshot and navigate to the file.

Snapshots help defend against accidental deletion.

Backup

I create a series of encrypted tar backups, that I store in an s3 bucket, to ensure that I have a remote / offsite copy of my data.

Data certainty

I have decided that I should defend against;

  1. single drive failure
    • defence: zfs mirrored drives and backups to s3
  2. accidental deletion
    • defence: snapshots, and backups to s3
  3. catastrophic failures
    • defence: backup to s3

Software

  • syncthing: syncing data between devices
  • avahi-daemon: access using <hostname>.local on local network
  • wiki.js: home wiki, for me and my partner