Background
I have been using Google Cloud to store my data such as photos, documents, and emails. I paid about $3 a month for the 100GB plan and recently it is about 80% full.

This prompted me to look for another solution: self-hosting a file server that I can access within my network. This will provide me with much more needed space than the 100GB I have now and also will provide external storage for my Proxmox and Kubernetes projects down the road.
One con of a self-hosted file server is that if the data is corrupted, we might not have a guaranteed way to recover it. This means a backup is crucial if the data stored is important (this can be looked into in the future). Other factors to consider would be power and security. Availability is not an issue for me as files may not be accessed that often.
Hardware
For the main server, I went with a Beelink Mini S12 Mini PC ($240). Here are some of the specifications:
For my file storage, I got a 2TB Samsung 870 EVO 2.5-inch SSD ($240).
Installation
I installed an Ubuntu server of version 22.04.5 LTS. Installation instructions can be found on their website.
I then freeze the version of Linux and do an apt update and upgrade
sudo apt-mark hold $(uname -r)
sudo apt update
sudo apt list --upgradable
sudo apt upgrade
Static IP configuration
By default, the network automatically uses DHCP to get an IP address from the router. I want mine to be fixed 192.168.1.10 for easy reference. Let’s configure a static IP address for it.
Before changing anything in my /etc/netplan/50-cloud-init.yaml file, the code was:
network:
ethernets:
enp1s0:
dhcp4: true
version: 2
After adding the IP address that we want to be static:
network:
ethernets:
enp1s0:
dhcp4: no
addresses: [192.168.1.10/24]
routes:
- to: default
via: 192.168.1.254
nameservers:
addresses: [1.1.1.1,8.8.8.8,8.8.8.4]
version: 2
dhcp4 is set to no, addresses are set to my static IP 192.168.1.10/24, routes indicate the internal gateway IP address which is 192.168.1.254. Nameservers are the DNS servers for name resolution, 1.1.1.1 for Cloudflare DNS and 8.8.8.8 for Google DNS.
As seen from the comments above, we need to disable the cloud-init by Ubuntu which will override this configuration at every reboot. Add to /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg, network: {config: disabled}.
Run sudo netplan try to apply the changes
Bluetooth configuration
Since our device comes with Bluetooth, it would be great if it can pair with my wireless keyboard.
We need to install bluez package
sudo apt install bluez -y
Entering the command bluetoothctl enters to a mini program.show shows no default controller available, which means my Bluetooth was not being detected.
Looking at the driver’s message by running: sudo dmesg|egrep -i 'blue|firm', there is an error bluetooth hci0: Direct firmware load for intel/ibt-0040-1050.sfi failed with error -2 which means that the Bluetooth firmware was not being loaded. Turns out it was not included in the /lib/firmware/intel/ directory.
To resolve this, we need to download it from elsewhere and do a reboot:
cd /lib/firmware/intel/
sudo wget https://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git/plain/intel/ibt-0040-1050.sfi
sudo wget https://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git/plain/intel/ibt-0040-1050.ddc
reboot
Entering bluetoothctl, we can scan on to scan for nearby devices to pair. pair <address> to initiate the pair. Then scan off to turn off Bluetooth scanning again.
To make it automatically pair upon turning on device, we install the bluez-tool and run the daemon:
sudo apt install bluez-tools -y
bt-agent --capability=NoInputNoOutput -d
--capability=NoInputNoOutput will prevent the agent for asking a confirmation for connection.
New drive configuration
We can take a look at our storage and initialize the SSD we have just added
sudo fdisk -l /dev/sda
sudo fdisk -l /dev/sdb
sudo gdisk /dev/sdb
n
1
2048 <enter>
3907029134 <enter>
8e00
w
Once done, we can use our newly formatted partition and make it into an LVM physical volume
sudo pvcreate /dev/sdb1
sudo pvdisplay /dev/sdb1
Next, we can add our physical volume to our newly created volume group to host a bunch of logical volumes that we will create later.
sudo vgcreate storage /dev/sdb1
sudo vgdisplay storage
Next, we can create a bunch of logical volumes, make the filesystem for each logical volume, and then mount them to our Linux
sudo lvcreate -n files -L 100G storage
sudo lvcreate -n photos -L 100G storage
ls /dev/mapper/storage*
sudo mkfs -t ext4 /dev/mapper/storage-files
sudo mkfs -t ext4 /dev/mapper/storage-photos
sudo mkdir /mnt/files
sudo mkdir /mnt/photos
sudo mount /dev/mapper/storage-files /mnt/files/
sudo mount /dev/mapper/storage-photos /mnt/photos/
df -h
The good thing about logical volume is we can always add space if we slowly start running out. To resize, we can run lvextend and resize2fs -p.
To make sure our logical volumes are mounted automatically when we boot into our system, update the fstab file as so:
/dev/mapper/storage-files /mnt/files ext4 defaults 0 2
/dev/mapper/storage-photos /mnt/photos ext4 defaults 0 2
The first 2 columns are the file system, and the mount point paths, the 3rd column is the type of file system. The 4th column is additional options that we set to defaults. The 5th column is for the dump command that we will disable. The 6th column sets the order in which the check disk commands run, 0 for no checking, 1 for the root drive, and 2 for other drives.
SSH configuration
To make login easier, we can do password-less authentication during ssh.
On our client's PC to connect, create our public and private RSA key pair via ssh-keygen. Follow the instructions shown by pressing enter.
Then copy the public key over to the server:
ssh-copy-id -i ~/.ssh/id_rsa.pub andre@192.168.1.10
Now we can ssh without entering the user password every time.
Network file server configuration
install the required server app and edit configurations related to the nfs server
apt install nfs-kernel-server
/mnt/files 192.168.1.106(rw,sync,no_subtree_check)
/mnt/photos 192.168.1.106(rw,sync,no_subtree_check)
sudo exportfs -rav
vim /etc/default/nfs-kernel-server
sudo systemctl restart nfs-kernel-server
configure the firewall for access
sudo ufw allow from 192.168.1.106 to any port 2049
sudo ufw enable
sudo ufw status
Ensure that the directory under /mnt/files and /mnt/photos are the ownership of the start user id of 1000
id 1000
sudo chown -R andre:andre /mnt/files
sudo chown -R andre:andre /mnt/photos
On the client PC that we want to connect to our nfs, we can install the nfs-common package
sudo apt install nfs-common
sudo mount -t nfs -vvv 192.168.1.10:/mnt/files /mnt/files
mount -t nfs4
echo "hello world" > /mnt/files/testfile
To automatically mount the NFS every time the client machine starts up, we can update the /etc/fstab file
192.168.1.10:/mnt/files /mnt/files nfs bg,rsize=8192,wsize=8192 0 0
Where bg is an option to run the mount in the background later if the mount fails. rsize and wsize sets the buffer size for reading and writing, by default it is 1024.
Samba file server configuration
create a new logical volume for samba and mount directory
sudo lvcreate -n sambashare -L 200G storage
sudo mkfs -t ext4 /dev/mapper/storage-sambashare
sudo mkdir /mnt/sambashare
sudo chown -R andre:andre /mnt/sambashare
sudo mount /dev/mapper/storage-sambashare /mnt/sambashare
install the required server apps and edit configurations
sudo apt install samba -y
sudo vim /etc/samba/smb.conf
sudo systemctl start smbd.service
now we can connect via Windows by providing the IP address of our Samba server to the file explorer
To mount samba via Linux as follows:
sudo mount -t cifs -o user=andre //192.168.1.10/sambashare /mnt/test
Conclusion
We have set up a mini pc capable of sharing storage through NFS and Samba. We can then access these storages via other Linux system via the NFS clients or on Windows samba share client.