NAS and Plex server

November 28, 2022

Overview

I recently changed up my home server setup. I now have a Proxmox box that hosts all of my machines except my combination NAS and media server. I decided to setup a dedicated machine to run my Plex server as well as serve up network storage via NFS. I wanted something with Intel Quick Sync support but also ECC memory. I settled on a C246 chipset motherboard with an Intel i3-9100 CPU and 32GB of ECC memory. So far, everything is working great, and I wanted to run through how I set it up.

Initial setup

I decided to use Fedora Server as the OS. I don’t have a need for a graphical interface, and I’ve really grown to like Fedora in general lately. The Cockpit web administration interface is top notch also. The initial partition layout is identical to my Fedora Workstation install post with the exception of no encryption. There is a nice graphic in that post to show the partitions. Once the install is complete, I set the hostname with # hostnamectl set-hostname --static nas.jlhinson.com.

BTRFS

I decided to use BTRFS for my storage file system. I’m just more familiar with it than ZFS, and it’s built in to the Linux kernel. I currently have a 3-disk RAID1 setup (soon to be 4) in use for my media storage. I have a traditional 2-disk RAID1 setup for general network storage (mostly backups). The steps below are for creating the 3-disk RAID1 setup but are almost identical for creating the 2-disk RAID1 setup.

# Use fdisk to create an empty GPT partition on all disks
# Create the RAID1 array
sudo mkfs.btrfs -f -m raid1 -d raid1 /dev/sdx /dev/sdx /dev/sdx
# before mounting, set the immutable flag to prevent anything being written if the array is not mounted
sudo chattr +i /mntpoint
# get UUIDs
sudo blkid
# add a line to the /etc/fstab file for only one of the disks
UUID=device-uuid    /mntpoint    btrfs    defaults    0 0
# mount and change ownership
sudo mount -a
sudo chown -R user:user /mntpoint

Snapper

I use Snapper to automatically create snapshots of root so that recovery is easier if something goes wrong. I’m not interested in being able to select a snapshot to boot from in Grub. If something does goes wrong, I can work with the snapshots manually for any recovery efforts needed. After installing Snapper and creating the root configuration, it automatically creates a nested subvolume at /.snapshots. This isn’t exactly the behavior I want, so I delete that subvolume, create a new top level subvolume, and mount it at the same location. This should all be done before enabling automatic timeline creation of snapshots. This example is specifically for the root subvolume, but it can easily be adapter to create custom configurations for other subvolumes. If you run into errors creating the snapshots, be sure the .snapshots folder has the SELinux type specified as snapperd_data_t.

sudo dnf install snapper
sudo snapper -c root create-config /
# edit /etc/snapper/configs/root to edit automatic snapshot creation and cleanup
# mount root BTRFS volume and create @snapshots subvolume
sudo btrfs subvolume delete /.snapshots
sudo mkdir /.snapshots
# edit /etc/fstab to auto mount @snapshots subvolume to /.snapshots
sudo mount -a
sudo systemctl enable --now snapper-timeline.timer
sudo systemctl enable --now snapper-cleanup.timer

NFS

I chose to use NFS for sharing the network storage space. It’s simple and fairly universally supported. The steps below should get it working. Be sure to allow the NFS server port through the firewall. If you want to be able to remotely list the available exports with showmount, you’ll also need to allow rpc-bind (111) and mountd (20048) in your firewall. This will be required for Proxmox to be able to add a NFS share from the server. While we’re talking about Proxmox, the NFS share you use with Proxmox will need to be writable by everyone since Proxmox will access the share as root and the NFS server will “squash” that to the user nobody. The other option would be to add the “no_root_squash” option to the NFS share, but that isn’t my preference.

# Install NFS
sudo dnf install nfs-utils
# add exports in the form of "/sharelocation  ip.add.re.ss/255.255.255.0(rw)
# the subnet range can be changes to a specific host and ro can be used for read only
sudo vim /etc/exports
# Enable and start the required services
sudo systemctl enable --now rpcbind.service
sudo systemctl enable --now nfs-server.service

To mount the NFS share on a Linux client, the nfs-utils package needs to be installed. The share can then be mounted with # mount -t nfs ip.add.re.ss:/sharelocation /mntpoint. It can also be mounted at boot my editing the /etc/fstab file and adding a line similar to ip.add.re.ss:/sharelocation /mntpoint nfs rw.

Plex

The Plex server install is very easy. Simply use wget to download the rpm file from the Plex site, make it executable, then install it with # yum install packagename. The systemd service should be automatically started and enabled. The rpm file also adds a repo file, but it is disabled by default. If you’d like to update Plex via dnf, simply enable the repo at /etc/yum.repos.d/plex.repo by setting enabled equal to 1. I don’t usually change many settings on the Plex server, but there are a few important ones for me. Under network options, I make sure to set a range of network addresses that are allowed without authentication when the server is signed out. This can be something like 192.168.8.0/255.255.255.0. I also set an interval for library updates as well as enabling the options to scan the library when changes are detected. Since I have an Intel Quick Sync CPU, I enabled the hardware acceleration option in the transcoder settings area.

I’ve setup Plex several times, but I had an issue with my TV intros not being detected after this install. I’m not sure why that happened, but I was able to fix it by manually selecting the analyze option for each season of each TV show. You can’t just run the analyze option on the whole show or whole library. This definitely isn’t ideal, but at least it’s working now.

Setup Postfix to use Gmail for sending email

We’ll need to install some programs first with # dnf install postfix mailx. Next, create the /etc/postfix/password_maps file with this line.

[smtp.gmail.com]:587 [email protected]:app-specific-password

Change the file to be rw only by root # chmod 600 /etc/postfix/password_maps then create the hashed version # postmap /etc/postfix/password_maps. In the /etc/postfix/main.cf file, comment out the smtp_tls_security_level line at the end of the file. Add these lines to the file in the relayhost section.

relayhost = [smtp.gmail.com]:587
smtp_tls_security_level = verify
smtp_tls_mandatory_ciphers = high
smtp_tls_verify_cert_match = hostname
smtp_sasl_auth_enable = yes
smtp_sasl_security_options = noanonymous
smtp_sasl_password_maps = hash:/etc/postfix/password_maps

The service can now be enabled/started with # systemctl enable --now postfix.service. To test things out, run echo 'This is just a test!' | mail -s "Test Subject" [email protected]. I’d also suggest editing /etc/aliases to add your personal email as the alias for root. This will ensure you receive important system notifications. Once that is done, be sure to run # newaliases. The mail program can now be used to send emails from the terminal for any status scripts you may want to create.

BTRFS scripts

I use this set of scripts on Github to schedule my recurring BTRFS scrubs. These scripts also allow for automating/scheduling balance, trim, and defragmentation actions. They can be installed with # dnf install btrfsmaintenance. To edit any of the configuration settings, just modify the file at /etc/sysconfig/btrfsmaintenance. Once you’ve listed all the BTRFS file systems to be scrubbed, enable the timer with # systemctl enable --now btrfs-scrub.timer.

I created a few custom scripts to check for errors daily and report the status every week. They depend on postfix being setup in the previous section. If you use these scripts, be sure to change the mount points to reflect your setup. The “.sh” files should be placed in /usr/local/bin and made executable with # chmod +x *.sh. The “.service” and “.timer” files should be placed in /etc/systemd/system. Don’t forget to enable and start the timers.

Daily error check

btrfs-check.sh

#!/bin/bash
#
# must run as root
#
day=`date +%D`
time=`date +%T`

if ! btrfs device stats -c /media-pool >> /dev/null
then
    echo "Email sent on $day at $time." > /etc/btrfs-stats-media-pool-email
    echo "" >> /etc/btrfs-stats-media-pool-email
    btrfs device stats /media-pool >> /etc/btrfs-stats-media-pool-email
    mail -Ssendwait -s "BTRFS ERROR ON MEDIA-POOL" [email protected] < /etc/btrfs-stats-media-pool-email
    rm /etc/btrfs-stats-media-pool-email
fi

if ! btrfs device stats -c /backup-pool >> /dev/null
then
    echo "Email sent on $day at $time." > /etc/btrfs-stats-backup-pool-email
    echo "" >> /etc/btrfs-stats-backup-pool-email
    btrfs device stats /backup-pool >> /etc/btrfs-stats-backup-pool-email
    mail -Ssendwait -s "BTRFS ERROR ON BACKUP-POOL" [email protected] < /etc/btrfs-stats-backup-pool-email
    rm /etc/btrfs-stats-backup-pool-email
fi

if ! btrfs device stats -c / >> /dev/null
then
    echo "Email sent on $day at $time." > /etc/btrfs-stats-root-email
    echo "" >> /etc/btrfs-stats-root-email
    btrfs device stats / >> /etc/btrfs-stats-root-email
    mail -Ssendwait -s "BTRFS ERROR ON ROOT" [email protected] < /etc/btrfs-stats-root-email
    rm /etc/btrfs-stats-root-email
fi

btrfs-check.service

[Unit]
Description=Check BTRFS devices for reported errors

[Service]
Type=oneshot
ExecStart=/usr/local/bin/btrfs-check.sh

btrfs-check.timer

[Unit]
Description=Check BTRFS devices for reported errors

[Timer]
OnCalendar=Mon..Fri *-*-* 13:30:00
OnCalendar=Sat,Sun *-*-* 06:30:00

[Install]
WantedBy=timers.target

Weekly summary

btrfs-summary.sh

#!/bin/bash
#
# must run as root
#
day=`date +%D`
time=`date +%T`

echo $'Weekly BTRFS status summary.\n ' > /etc/btrfs-summary-email
echo "Sent on $day at $time." >> /etc/btrfs-summary-email
echo $'\n----------------------------------------------------\n' >> /etc/btrfs-summary-email
echo $'/\n' >> /etc/btrfs-summary-email
btrfs filesystem usage / >> /etc/btrfs-summary-email
echo $'\nScrub status:' >> /etc/btrfs-summary-email
btrfs scrub status / >> /etc/btrfs-summary-email
echo $'\nErrors:' >> /etc/btrfs-summary-email
btrfs device stats / >> /etc/btrfs-summary-email
echo $'\n----------------------------------------------------\n\n/media-pool\n' >> /etc/btrfs-summary-email
btrfs filesystem usage /media-pool >> /etc/btrfs-summary-email
echo $'\nScrub status:' >> /etc/btrfs-summary-email
btrfs scrub status /media-pool >> /etc/btrfs-summary-email
echo $'\nErrors:' >> /etc/btrfs-summary-email
btrfs device stats /media-pool >> /etc/btrfs-summary-email
echo $'\n----------------------------------------------------\n\n/backup-pool\n' >> /etc/btrfs-summary-email
btrfs filesystem usage /backup-pool >> /etc/btrfs-summary-email
echo $'\nScrub status:' >> /etc/btrfs-summary-email
btrfs scrub status /backup-pool >> /etc/btrfs-summary-email
echo $'\nErrors:' >> /etc/btrfs-summary-email
btrfs device stats /backup-pool >> /etc/btrfs-summary-email
echo $'\n----------------------------------------------------' >> /etc/btrfs-summary-email

mail -Ssendwait -s "BTRFS WEEKLY SUMMARY" [email protected] < /etc/btrfs-summary-email
rm /etc/btrfs-summary-email

btrfs-summary.service

[Unit]
Description=Generate BTRFS status summary

[Service]
Type=oneshot
ExecStart=/usr/local/bin/btrfs-summary.sh

btrfs-summary.timer

[Unit]
Description=Generate BTRFS status summary

[Timer]
OnCalendar=Sat *-*-* 07:30:00

[Install]
WantedBy=timers.target

Conclusion

So far, I’m enjoying having a dedicated NAS/Plex machine with everything else hosted on a Proxmox box. Some of this is very similar (identical) to another post, but I thought it would be best to list everything I did in one place. I may try to reference this as needed from other posts and just keep this updated as things change. As always, I hope this was helpful or at least sparked some new ideas.