Raspberry Pi home server

2023-12-24

When we lived in London we had an HP Compaq Elite 8300 desktop tower that we used as a home server. The server ran Ubuntu minimal server 18.04. I used the server to back up important documents, and used Samba to connect our laptops to access music, movies etc.

The server was very convenient for me, but it was a bit awkward for others to connect to if they weren’t familiar with network shares or SSH. Additionally, the server was quite power hungry. I set it up to wake on LAN, but this meant I was the only one in the house who could turn it on.

In our new flat I aimed to upgrade the home server setup to reduce power consumption, increase storage capacity, and improve accessibility for less techy people.

Hardware

The RPi is connected to the router via a short ethernet cable, and to the SSD via USB. The RPi boots from a 64 GB Micro SD.

I used zipties to mount the three bits of hardware onto a section from a plastic crate, the kind used to hold fruits and vegetables. I then hung the “rack” under a gateleg table using screw-in hooks.

To play music in the living room the RPi is connected to a Yamaha CRX-M170 micro Hi-Fi system via a 3.5 mm to phono cable. Unfortunately, the 3.5 mm jack in the RPi 4 is terrible. The sound comes through very tinny with lots of interference. To get decent audio quality I had to buy an IQaudIO DAC Pro hat for the RPi. The DAC Pro connects via the GPIO pins to the RPi.

Server setup mounted to plastic tray 'rack'.

Software

We don’t have internet in our new flat, instead we just use our mobile phones. The GL-A1300 router has a USB port that can connect an Android phone to use it for USB tethering. On the rare occasion that we need to connect the home LAN network to the internet we can tether a phone, but mostly we can remain offline.

The RPi runs Raspberry Pi OS Lite version 2023-12-05. This version of the OS doesn’t include a desktop environment and works well as a server.

I installed Jellyfin to serve video and audio from the RPi to all our devices. Jellyfin has an app for Android that works quite well, and a web app that is accessible over the local network that works well on laptops. Jellyfin can cast directly to a TV using DLNA.

I installed Mopidy to play audio on the Hi-Fi from the RPi. I installed the Mopidy MPD extension , which allows MPD clients like M.A.L.P. to connect to the Mopidy instance. I installed the Mopidy MusicBox extension to provide a web client music player. I ran into a problem where Raspberry Pi OS wouldn’t let me install any python packages via pip, including the Mopidy Jellyfin extension. All I had to do to fix the issue was to delete this file:

sudo rm -rf /usr/lib/python3.11/EXTERNALLY-MANAGED

It took me some time to figure out how to get Mopidy and Jellyfin to both access .m3u8 playlists. Playlists are stored in /var/lib/jellyfin/data/playlists/. Playlists are created in Jellyfin and stored as .xml files, which Mopidy can read with the Mopidy Jellyfin extension . Mopidy doesn’t have the ability to create playlists, and Jellyfin will happily add songs to an existing .m3u8 playlist; clearly this feature isn’t fully matured in either mopidy or Jellyfin. It seems others have had problems understanding the Jellyfin-mopidy playlist situation, judging by various Github issues .

I installed Samba to make it easier for others to add media from their devices, and I installed qBittorrent (-nox variant with no X-server) to download media directly onto the server when it’s connected to the internet.

To create backups I have an additional 1TB SSD which is synced using rsync once a week. I have a third SSD that is synced every month, which I keep at work.

I installed Calibre to manage ebooks on the server. I run Calibre as a server using systemd. It would be wonderful if Jellyfin had a Calibre plugin, something that could read the Calibre database and serve it within the Jellyfin app, but I haven’t found anything yet. For now, I have placed the Calibre database in the Jellyfin directory structure, in /mnt/media/Books/Books/. Mostly this system works fine. I use Calibre to download book metadata. Annoyingly, Calibre uses cover.jpg as the filename for book cover images, while Jellyfin uses folder.jpg. I wrote a bash script to run periodically that copies missing cover image files and renames them:

#!/usr/bin/env bash

# Define directory to search for PNG files
search_dir="/Users/johngodlee/Desktop/test"

# Find PNG files recursively
find "$search_dir" -type f -name "*.png" | while read -r png_file ; do
    # Get directory and filename without extension
    dir_path="${png_file%/*}"
    base_name="${png_file##*/}"
    base_name_without_ext="${base_name%.png}"
    
    # Define path for converted JPG 
    jpg_file="$dir_path/$base_name_without_ext.jpg"
    
    # Convert PNG to JPG using ImageMagick
    convert "$png_file" "$jpg_file" 

    # Remove original PNG 
    rm "$png_file"
done

# Find cover.jpg files recursively
find "$search_dir" -type f -name "cover.jpg" | while read -r cover_file; do
    # Get directory of cover.jpg 
    dir_path=$(dirname "$cover_file")
    
    # Check if folder.jpg exists in same directory
    if [ ! -f "$dir_path/folder.jpg" ]; then
        # Rename cover.jpg to folder.jpg
        cp "$cover_file" "$dir_path/folder.jpg"
        echo "Copied $cover_file to folder.jpg"
    else
        echo "folder.jpg already exists in $dir_path, skipping $cover_file"
    fi
done

# Find folder.jpg files recursively
find "$search_dir" -type f -name "folder.jpg" | while read -r folder_file; do
    # Get directory of folder.jpg
    dir_path=$(dirname "$folder_file")
    
    # Check if cover.jpg exists in same directory
    if [ ! -f "$dir_path/cover.jpg" ]; then
        # Rename folder.jpg to cover.jpg
        cp "$folder_file" "$dir_path/cover.jpg"
        echo "Copied $folder_file to cover.jpg"
    else
        echo "cover.jpg already exists in $dir_path, skipping $folder_file"
    fi
done