Venturing into freeBSD: Homeserver attempt 2
Why freeBSD?#
ZFS. raidz2 recommended but we will use raidz1 in our experiment! Link for more info.
HW setup#
Here are the main parts for the build:
- N5105 Motherboard (CPU attached)
- Innovision 4bay NAS case comes with individual HDD bay LEDs! Spectacle when they spin up!
The N5105 is an Intel Celeron chip with a TDP of just 10W (in the RPI range..). It also has the QuickSync feature for hardware assisted Plex transcoding. :)
I got an 8GB RAM. The board accepts DDR4 Laptop SO-DIMM sticks. And also got a 120GB NVME SSD for the boot drive.
The case comes with the PSU included. SAS HDDs are cheaper, but I wasn't sure if the case support them. So went for 4x SATA 4TB ones.
SW setup#
As mentioned we'll be using freeBSD. This OS does not support docker. But has the concept of Jails. It is the same principle (container isolation) but predates docker.
Using a jail manager provides a simpler interface to managing jails. We'll use the bastille jail manager. For the following features:
- auto NAT! (using PF; whole new rabbithole; we'll learn just enough to get by..)
- easy and intuitive recipes/templates to automate spinning up jails (very useful in our complex case!)
We'll use no hostname resolution. Instead we'll refer each app using it's jail's hardcoded IPs (that's internally NAT'd)
Pre-requisites#
We add our user to sudo. In this article I'll be referring to user ajit
as the main user. The username doesn't matter, what's important is the uid
. I've set this to 1001
to be consistent with the default first user created in freeBSD host. See the templates below for clarity.
Remember to add this user to group wheel
when you install the host system or afterwards. Then:
su -
install pkg sudo
visudo
Uncomment the %wheel so it looks like this :
## Allows people in group wheel to run all commands
%wheel ALL=(ALL) ALL
We have to enable mDNS so that our server can be reachable using it's hostname (eg freesrv.local):
sudo pkg install openmdns
sudo sysrc mdnsd_enable=yes
sudo sysrc mdnsd_flags=igc0 #indicate eth interface
sudo service mdnsd start
In the flag above replace igc0
with your ethernet interface.
Enable powerd
to make the system run more power efficient. See here.
ZFS setup#
We begin by setting up our ZFS storage. Refer the handbook and create media
and appdata
datasets. Now create the following directory structure:
cd <..>/media
mkdir medialibrary
mkdir medialibrary/movies
mkdir medialibrary/tv
mkdir torrents
cd <..>/appdata
mkdir radarr sonarr sabnzbd
After initialised, I was left with 10TB usable storage!
Bastille jails setup#
Run this to automatically setup bastille. It'll also setup PF with rule for ssh.
sudo bastille setup
If you have external ZFS mounts on your system but want bastille to use the UFS filesystem (used by root):
sudo bastille setup ufs
We also need to allow mDNS (hostname resolution). To do this add the following to the /etc/pf.conf
:
pass in inet proto udp from any to any port = 5353 keep state
Now:
sudo service pf start
You will need to re-initiate the ssh connection.
Finally to setup the jails issue the following commands (templates at the end of this article):
sudo bastille create radarrJail 14.0-RELEASE 10.17.89.50
sudo bastille template radarrJail bastillebsd-templates/radarr
sudo bastille create sonarrJail 14.0-RELEASE 10.17.89.51
sudo bastille template radarrJail bastillebsd-templates/sonarr
sudo bastille create sabnzbdJail 14.0-RELEASE 10.17.89.52
sudo bastille template radarrJail bastillebsd-templates/sabnzbd
sudo bastille create jellyfinJail 14.0-RELEASE 10.17.89.53
sudo bastille template radarrJail bastillebsd-templates/jellyfin
sudo bastille create homerJail 14.0-RELEASE 10.17.89.54
sudo bastille template homerJail bastillebsd-templates/homer
If everything goes to plan, you should have an accessible *ARR suite running, with the correct permisions.
Additional NOTE: Set the media files to be globally readable. This is so that we can run jellyfin
as any user. Goto Radarr Settings -> Media Management (Show Advanced) -> Permissions -> Set Permissions (checked) -> chmod Folder [755].
TODO: Implement the equivalent of Watchtowerr application that auto updates the container packages on a periodic basis (ie weekly). To achieve this I could incorporate the cron and some shell scripting (but my scripting skills are not upto scratch). A rudimentary approach is outlined in this forum post. Unfortunately it restarts the containers regardless if there was an update, which I think is wasteful.
Templates dump#
radarr:
CMD pw useradd -u 1001 -n ajit -c "Ajit" -s csh -m -w random
CMD mkdir /mnt/media
CONFIG set allow.mlock=1;
CONFIG set ip6=inherit;
RDR tcp 7878 7878
RESTART
PKG radarr
MOUNT /storage/appdata/radarr /usr/local/radarr nullfs rw 0 0
MOUNT /storage/media /mnt/media nullfs rw 0 0
RESTART
SYSRC radarr_enable="YES"
SERVICE radarr start
CMD chown -R ajit:ajit /var/run/radarr
CMD sed -i -r s'/radarr_user:="radarr"/radarr_user:="ajit"/' /usr/local/etc/rc.d/radarr
CMD sed -i -r s'/radarr_group:="radarr"/radarr_group:="ajit"/' /usr/local/etc/rc.d/radarr
SERVICE radarr restart
sonarr:
CMD pw useradd -u 1001 -n ajit -c "Ajit" -s csh -m -w random
CMD mkdir /mnt/media
CONFIG set allow.mlock=1;
CONFIG set ip6=inherit;
RDR tcp 8989 8989
RESTART
PKG sonarr
MOUNT /storage/appdata/sonarr /usr/local/sonarr nullfs rw 0 0
MOUNT /storage/media /mnt/media nullfs rw 0 0
RESTART
SYSRC sonarr_enable="YES"
SERVICE sonarr start
CMD chown -R ajit:ajit /var/run/sonarr
CMD sed -i -r s'/sonarr_user:="sonarr"/sonarr_user:="ajit"/' /usr/local/etc/rc.d/sonarr
CMD sed -i -r s'/sonarr_group:="sonarr"/sonarr_group:="ajit"/' /usr/local/etc/rc.d/sonarr
SERVICE sonarr restart
sabnzbd:
CMD pw useradd -u 1001 -n ajit -c "Ajit" -s csh -m -w random
CMD mkdir -p /mnt/media/torrents
CONFIG set ip6=inherit;
RDR tcp 8080 8080
RESTART
PKG sabnzbd
MOUNT /storage/appdata/sabnzbd /usr/local/sabnzbd nullfs rw 0 0
MOUNT /storage/media/torrents /mnt/media/torrents nullfs rw 0 0
RESTART
SYSRC sabnzbd_enable="YES"
SERVICE sabnzbd start
CMD chown -R ajit:ajit /var/run/sabnzbd
CMD sed -i -r s'/sabnzbd_user:=_sabnzbd/sabnzbd_user:="ajit"/' /usr/local/etc/rc.d/sabnzbd
CMD sed -i -r s'/sabnzbd_group:=_sabnzbd/sabnzbd_group:="ajit"/' /usr/local/etc/rc.d/sabnzbd
SERVICE sabnzbd restart
jellyfin:
CMD mkdir -p /mnt/data/movies
CMD mkdir -p /mnt/data/tv
CMD mkdir -p /usr/local/etc/pkg/repos
CMD echo 'FreeBSD: { url: "pkg+http://pkg.FreeBSD.org/${ABI}/latest" }' > /usr/local/etc/pkg/repos/FreeBSD.conf
CONFIG set allow.mlock=1;
CONFIG set ip6=inherit;
RDR tcp 8096 8096
RESTART
PKG jellyfin
MOUNT /storage/media/medialibrary/movies /mnt/data/movies nullfs rw 0 0
MOUNT /storage/media/medialibrary/tv /mnt/data/tv nullfs rw 0 0
RESTART
SYSRC jellyfin_enable="YES"
SERVICE jellyfin start
homer:
CONFIG set ip6=inherit;
RDR tcp 80 80
RESTART
PKG apache24
CMD curl -LO https://github.com/bastienwirtz/homer/releases/latest/download/homer.zip
CMD unzip -o homer.zip -d /usr/local/www/apache24/data
CMD cp /usr/local/www/apache24/data/assets/config.yml.dist /usr/local/www/apache24/data/assets/config.yml
CP config.yml /usr/local/www/apache24/data/assets/
SYSRC apache24_enable="YES"
SYSRC apache24_flags=""
CMD httpd -t
SERVICE apache24 start
homer config.yml
. Place this file beside the homer template.
---
# Homepage configuration
# See https://fontawesome.com/v5/search for icons options
title: "FreeSRV dashboard"
subtitle: "Homer"
logo: "logo.png"
# icon: "fas fa-skull-crossbones" # Optional icon
header: true
footer: '<p>Created with <span class="has-text-danger">❤️</span> with <a href="https://bulma.io/">bulma</a>, <a href="https://vuejs.org/">vuejs</a> & <a href="https://fontawesome.com/">font awesome</a> // Fork me on <a href="https://github.com/bastienwirtz/homer"><i class="fab fa-github-alt"></i></a></p>' # set false if you want to hide it.
# Optional theme customization
theme: default
colors:
light:
highlight-primary: "#3367d6"
highlight-secondary: "#4285f4"
highlight-hover: "#5a95f5"
background: "#f5f5f5"
card-background: "#ffffff"
text: "#363636"
text-header: "#ffffff"
text-title: "#303030"
text-subtitle: "#424242"
card-shadow: rgba(0, 0, 0, 0.1)
link: "#3273dc"
link-hover: "#363636"
dark:
highlight-primary: "#3367d6"
highlight-secondary: "#4285f4"
highlight-hover: "#5a95f5"
background: "#131313"
card-background: "#2b2b2b"
text: "#eaeaea"
text-header: "#ffffff"
text-title: "#fafafa"
text-subtitle: "#f5f5f5"
card-shadow: rgba(0, 0, 0, 0.4)
link: "#3273dc"
link-hover: "#ffdd57"
# Optional navbar
# links: [] # Allows for navbar (dark mode, layout, and search) without any links
links:
- name: "Contribute"
icon: "fab fa-github"
url: "https://github.com/bastienwirtz/homer"
target: "_blank" # optional html a tag target attribute
- name: "Wiki"
icon: "fas fa-book"
url: "https://www.wikipedia.org/"
# this will link to a second homer page that will load config from additional-page.yml and keep default config values as in config.yml file
# see url field and assets/additional-page.yml.dist used in this example:
#- name: "another page!"
# icon: "fas fa-file-alt"
# url: "#additional-page"
# Services
# First level array represent a group.
# Leave only a "items" key if not using group (group name, icon & tagstyle are optional, section separation will not be displayed).
services:
- name: "Applications"
icon: "fas fa-cloud"
items:
- name: "Jellyfin"
logo: "assets/tools/sample2.png"
subtitle: "Media Player"
tag: "app"
url: "http://freesrv.local:8096/"
target: "_blank" # optional html a tag target attribute
- name: "Radarr"
logo: "assets/tools/sample.png"
subtitle: "Movies"
tag: "app"
url: "http://freesrv.local:7878/"
target: "_blank" # optional html a tag target attribute
- name: "Sonarr"
logo: "assets/tools/sample.png"
subtitle: "TV"
tag: "app"
url: "http://freesrv.local:8989/"
target: "_blank" # optional html a tag target attribute
- name: "Sabnzbd"
logo: "assets/tools/sample.png"
subtitle: "Usenet"
tag: "app"
url: "http://freesrv.local:8080/"
target: "_blank" # optional html a tag target attribute