This article is a guide on how to set up a simple network-attached storage (NAS) computer based on a Raspberry Pi and external hard drives connected via USB. To follow along basic understanding of how to install and use a Linux OS is required, but to get a setup like this working no crazy technical knowledge is needed. If you want to know why some possibly odd choices were made, check out the accompanying blog entry. 😊
Important: This tutorial contains lots of shell commands to make things easier. Don’t blindly copy them as they assume my setup and don't paste multiple commands as blobs at once!
👓The whole picture
The goal is to build a NAS with full redundancy and data encryption using a Pi that boots from a USB disk. To achieve redundancy I use two identical HHDs as storage which are combined to a single block device as RAID using mdadm . Reads and writes are cached with bcache which will use a separate partition on the boot SSD to keep recently accessed data. Finally ecryptfs is used to encrypt all stored files and file names.
🚀Installing the OS
Download the 64bit image of Ubuntu Server for Raspberry Pi from here and burn it to an SD-Card. Boot up the Pi and install the software tools for the Pi with the following command:
sudo apt install libraspberrypi-bin
Now add program_usb_boot_mode=1 to the /boot/config.txt file in a new line. Reboot the Pi with sudo reboot , don’t just pull the power plug! To check whether the firmware is now able to boot from USB devices run vcgencmd otp_dump and look if you can spot the line 17::3020000a .
Now clone both partitions on the SD-Card onto your USB boot device. As we’ll be using the boot device as read-cache too, we need to make space for a third partition. The easiest way to do this is to use Gparted on a Linux desktop machine.
If you only have a Windows computer you can install Win32DiskImager to clone the SD-Card and follow the next few steps: You can check now if booting from USB works, by only connecting the USB drive to the Pi. (As written in my blog entry I ended up still needing the SD-Card later though.) Then boot the Pi from the SD-Card and connect the USB drive. Use lsblk to list all block devices. There should be an unmounted /dev/sdX with the same size as your USB drive, where X is a lowercase letter, probably “a”. This “block device” is the representation of the hard drive in software. Shrink the filesystem of the “writable” partition (not the “boot” partition) on the USB drive to the appropriate size with resize2fs and then shrink the partition using cfdisk . My drive is 256GB large and I will leave 100GB for Ubuntu and use the rest as cache.
sudo resize2fs /dev/sda 100G sudo cfdisk /dev/sda
Just create a simple primary Linux partition filling the remaining free space on the disk using all default options in cfdisk or Gparted. Remember that you have to write out the changes to the disk before quitting, or nothing will be changed. You can use e2fsch to check and repair the integrity of the resized filesystem. Here you have to provide the name of the partition, which will be the name of the device with a single digit number as seen in lsblk .
If you want the handy raspi-config tool, you need to install it by downloading it as a package and install it manually. Go to the archive page and copy the link to the most recent version. As of writing, this is raspi-config_20210604_all.deb . Download it with wget to /temp and install its dependencies, then the package itself.
wget https://archive.raspberrypi.org/debian/pool/main/r/raspi-config/raspi-config_20210604_all.deb -P /tmp sudo apt-get install libnewt0.52 whiptail parted triggerhappy lua5.1 alsa-utils -y sudo apt-get install -fy sudo dpkg -i /tmp/raspi-config_20210604_all.deb
Now you can run sudo raspi-config to get the interactive configuration tool.
📡Connecting to Wifi
I personally only set up wifi for some early experiments. I would recommend a wired connection for your NAS to your switch or router. First, get the name of your wifi network interface by running ls /sys/class/net . It is probably called wlan0 . Then edit the file /etc/netplan/50-cloud-init.yaml with root privileges and add a wifis section.
network: ethernets: eth0: dhcp4: true optional: true version: 2 wifis: MyNetworkInterface: optional: true access-points: "My Wifi SSID": password: "My Wifi password" dhcp4: true
Replace “MyNetworkInterface”, “My Wifi SSID” and “My Wifi password” with your information and do a reboot.
🔗Setting up RAID
First both drives need to be formatted. Assuming they are called sdb and sdc , just run the commands below. Now run cfdisk for each drive and create a primary partition of type “Linux Raid” which fills all available space.
sudo parted /dev/sdb mklabel gpt sudo parted /dev/sdc mklabel gpt sudo cfdisk /dev/sdb sudo cfdisk /dev/sdc
Both drives are now prepared to be combined into a single logical one using RAID. Install mdadm and run it to create a new RAID block device from the first (and only) partition on each of the two drives. Executing with --examine shows the health of your RAID. Immediately after creation mdadm will start syncing both drives block by block. Depending on the drive's size this can take a long time. You can check the process by looking at the contents of /proc/mdstat . It took my setup more than two days, but luckily this background process is paused and resumed automatically whenever you use the RAID. Therefore you can proceed with the next steps without having to wait for it to finish.
sudo apt-get install mdadm sudo mdadm --verbose --create /dev/md0 --level=raid1 --raid-devices=2 /dev/sd[bc]1 sudo mdadm --examine /dev/sdb1 /dev/sdc1 cat /proc/mdstat
Caching will be handled by bcache . It will combine the RAID disk md0 and the third partition on the boot disk into a single cached (again logical) block device. The process is very simple: First create the cache disk, format it with a filesystem and make it mount on startup. The mountpoint will be a directory inside of the ubuntu user's home.
sudo make-bcache -B /dev/md127 -C /dev/sda3 cat /sys/block/bcache0/bcache/state sudo mkfs.ext4 /dev/bcache0 mkdir ~/nas mkdir ~/nas/nasdrive-mnt sudo e2label /dev/bcache0 cached-nasdrive
Directly after creating the cached block device it should show up as /dev/bcache0 and /sys/block/bcache0/bcache/state should contain “clean”. If everything is good, prepare the mountpoint and label the drive. I just named it “cached-nasdrive”. We don’t need to label the physical drives or the RAID device as the system will never have to mount them automatically.
Add the following line at the end of /etc/fstab to mount the cached block device as a filesystem in our prepared directory on startup.
LABEL=cached-nasdrive /home/ubuntu/nas/nasdrive-mnt ext4 defaults 0 2
Now you can run mount -a to test if the entry works without issues. The folder nasdrive-mnt should now be a portal to the cached RAID. This can be checked using mountpoint , also you should see a folder called “lost+found” indicating that this is the base of an ext4 filesystem.
sudo mount -a mountpoint ~/nas/nasdrive-mnt
🔐Encrypting your data
To encrypt the data stored on the NAS I used ecryptfs which handles directory level encryption on a user by user basis. As ecryptfs seems to be very inflexible about its directory structure an additional user is needed, whose home directory will be mounted on the “cached-nasdrive”. To allow for multiple network shares with different encryption keys making them completely separate from each other, several such users can be created repeating the steps below. If you don’t need such high security you can stick to a single encryption user and let SAMBA handle the access control.
The user is called “nasuser” and needs a secure password, as it will be used to safely store the encryption key. After installing the required tools log in as the newly created user and run the interactive setup program. Do as the program instructs you and print the encryption key to screen and create a secure copy! This will be needed if you ever have to recover your data on a different machine. You should see that a Private and two hidden folders have appeared in the home directory. Finally, remove the file auto-unmount to allow access through SAMBA.
sudo adduser nasuser sudo apt-get install ecryptfs-utils su nasuser ecryptfs-setup-private ecryptfs-unwrap-passphrase .ecryptfs/wrapped-passphrase rm ~/.ecryptfs/auto-umount
After logging in and out the Private folder should now be a mountpoint. When creating a file inside of it, another one should appear in the hidden .Private directory with an illegible name. Now that this is all set up logout back to the ubuntu user to move all of the nasuser’s files to the hard drives.
Create a directory for the nasuser’s home folder and then copy all of its files there.
mkdir ~/nas/nasdrive-mnt/nasdrive-home sudo cp -pvau /home/nasuser/ /home/ubuntu/nas/nasdrive-mnt/nasdrive-home
Now we only need to mount this new folder with all the files copied to it as a replacement for the current home directory. To make a portal from one directory to another one a bind mount can be used. The fstab entry looks like this:
/home/ubuntu/nas/nasdrive-mnt/nasdrive-home/nasuser /home/nasuser none defaults,bind 0 0
After running mount -a the home directory /home/nasuser should look unchanged but be a mountpoint.
📂Creating a network share
If you only want to connect from a Linux system as a client you probably can stop now as adding an SFTP connection as a network share is pretty straightforward. Else you can install SAMBA to be able to connect to your NAS from multiple devices on different platforms.
Add a SAMBA password for the nasuser by running sudo smbpasswd -a nasuser . Then edit /etc/samba/smb.conf and add the following block of text in the section for network shares.
[nasusershare] comment = Nasuser Folder path = /home/nasuser/Private valid users = nasuser read list = nasuser write list = nasuser force directory mode = 0755 create mask = 0755 force create mode = 0755 directory mask = 0755 inherit permissions = yes store dos attributes = no public = yes ea support = no inherit acls = yes browseable = yes writeable = yes printable = no
Finally, restart the SAMBA service using sudo systemctl restart smbd.service . You should be able to connect to your NAS via its local IP from your Windows or Linux machine, phone, or iPad.