Home server project
Planning stage:
- Decide hardware to use:
- I had a 4 GB RAM Raspberry Pi 5 which I bought with the intention to set up as a retro gaming console. I did so but wasn’t really using much - so I decided to repurpose it as something more practical.
- Needed storage space so ordered a 1TB SATA drive + mounting board.
- Decide software to use:
- Quick bit of research found an official Raspberry Pi image for Ubuntu Server - I’ve used Linux for years so that was perfect.
- https://ubuntu.com/download/raspberry-pi
- Some more research suggested Docker as an ideal way to run a few services. (My primary use case was a media centre but if I was going to set up a server I decided I might as well extent its functionality beyond that.)
- Quick bit of research found an official Raspberry Pi image for Ubuntu Server - I’ve used Linux for years so that was perfect.
- Plan Docker containers:
- Jellyfin - similar to Plex but fully open source.
- Nextcloud - another open source application: a self hosted cloud drive.
- Postgres - database for Nextcloud
- qBittorrent - my intention was to download directly on the server to avoid copying lots of media from my PC.
- Glances - monitoring service.
Initial set up:
- Installing Ubuntu Server on the Pi’s SD card was easy - used an application called Raspberry Pi imager.
- Can enable SSH + user details during imaging.
- Post install can either scan network from another machine using nmap to identify its IP or use the server briefly plugged into a monitor. ip addr
- To set a static IP I edited:
$ cat /etc/netplan/50-cloud-init.yaml
network:
version: 2
ethernets:
eth0:
dhcp4: no
addresses:
- 192.168.0.172/24
gateway4: 192.168.0.1
nameservers:
addresses: [1.1.1.1, 8.8.8.8]
So the IP is 192.168.0.172
- Install Docker:
sudo apt get update
sudo apt get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
# Add the repository to Apt sources:
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
- Set up directories for the various services:
$ ls -la /mnt/storage/
total 48
drwxr-xr-x 9 calum gpio 4096 Sep 16 15:37 .
drwxr-xr-x 4 root root 4096 Sep 18 15:23 ..
drwxr-xr-x 3 calum gpio 4096 Sep 4 18:57 glances
drwxr-xr-x 4 calum gpio 4096 Sep 4 18:55 jellyfin
drwxr-xr-x 2 calum gpio 16384 Sep 4 18:36 lost+found
drwxr-xr-x 3 root root 4096 Sep 14 21:29 media
drwxr-x--- 4 www-data www-data 4096 Sep 4 18:56 nextcloud
drwxr-xr-x 3 calum gpio 4096 Sep 4 18:56 postgres
drwxr-xr-x 7 calum calum 4096 Sep 14 22:51 qbittorrent
- Create Docker compose file. I did get help from ChatGPT to do this! Mnt points defined in this file are important.
$ cat docker-compose.yml
services:
jellyfin:
image: jellyfin/jellyfin:latest
init: true
container_name: jellyfin
user: "1000:1000"
environment:
- TZ=Europe/London
- PUID=1000
- PGID=1000
volumes:
- /mnt/storage/jellyfin/config:/config
- /mnt/storage/jellyfin/media:/media
ports:
- "8096:8096"
restart: unless-stopped
nextcloud:
image: nextcloud:latest
init: true
container_name: nextcloud
environment:
- POSTGRES_DB=nextcloud
- POSTGRES_USER=nextcloud
- POSTGRES_PASSWORD=nextcloud
- POSTGRES_HOST=postgres
- POSTGRES_PORT=5432
- TZ=Europe/London
depends_on:
- postgres
volumes:
- /mnt/storage/nextcloud/config:/var/www/html/config
- /mnt/storage/nextcloud/data:/var/www/html/data
ports:
- "8080:80"
restart: unless-stopped
postgres:
image: postgres:14
init: true
container_name: postgres
environment:
- POSTGRES_USER=nextcloud
- POSTGRES_PASSWORD=nextcloud
- POSTGRES_DB=nextcloud
- TZ=Europe/London
volumes:
- /mnt/storage/postgres/data:/var/lib/postgresql/data
restart: unless-stopped
qbittorrent:
image: linuxserver/qbittorrent:latest
container_name: qbittorrent
environment:
- PUID=1000
- PGID=1003
- TZ=Europe/London
- WEBUI_PORT=8080 # Container port (mapped to host 8081)
- WEBUI_LISTENING_IP=0.0.0.0 # Listen on all IPv4 interfaces
volumes:
- /mnt/storage/qbittorrent/config:/config
- /mnt/storage/media/qbittorrent/completed:/downloads # Completed downloads
- /mnt/storage/qbittorrent/incomplete:/downloads/incomplete # Incomplete downloads
ports:
- "6881:6881"
- "6881:6881/udp"
- "8081:8080" # Host port : Container port
restart: unless-stopped
glances:
image: nicolargo/glances:latest-full
container_name: glances
network_mode: host
pid: "host"
environment:
- GLANCES_OPT=-w
- GLANCES_EXPORT=influxdb
- GLANCES_INFLUXDB_HOST=192.168.0.120
- GLANCES_INFLUXDB_PORT=8086
- GLANCES_INFLUXDB_DB=glances
- GLANCES_INFLUXDB_USER=admin
- GLANCES_INFLUXDB_PASSWORD=admin
volumes:
- /mnt/storage/glances/config:/glances
- /var/run/docker.sock:/var/run/docker.sock
restart: unless-stopped
tty: true
stdin_open: true
- Spin up the server!
docker compose up -d
- Add SSH keys from PC to server for password less connection.
Further set up:
- Get content into Jellyfin libraries.
- Had media on my PC - SCPed files across into the appropriate directories.
- Longer term strategy is to use the qBittorrent service to download directly to the Pi. Maaaybe, see later.
- Access the webUIs for the various services to eg set up users, configuration.
Issues faced:
- No user could log into Jellyfin!!
- Troubleshot this and it turned out to a plugin I’d added. Decided I didn’t need it after all so resolved the issue by removing said plugin.
- Encountered intermittent access issues with Jellyfin (UI loaded but server connection refused). Mostly related to permissions of the directories being accessed.
- SD card corruption. Twice my card failed on me and I had to reflash it and rebuild the sever. Using Docker made this fairly painless though.
- Transcoding. Some video file formats / codecs require transcoding when playing in Jellyfin. I was aware of this but hadn’t really planned for it. I first noticed when I tried to watch a certain film and saw my CPU use rocket to basically 100%. The video buffered a lot too and was pretty much unwatchable.
- This created a problem for my intended workflow of downloading content directly to the Piserver: file converting is too heavy a task to run on the Pi.
- Plan was to scan new videos to check whether they are likely to need transcoding.
- The script I had to do this proved totally inaccurate and can’t be relied upon.
- Settled on a mostly manual workflow, automating moving files between PC and server in a couple of places. Confirming transcode / direct play status of content currently needs to be done manually.
- I still have a qBittorrent container in my stack but will most likely remove this if I can’t find a better workflow than above.
- Office applications in Nextcloud. “Out of the box”, Nextcloud is more like Dropbox than Google Drive but there are a wide range of apps available to add to it. I tried adding one called OnlyOffice but it was a bit of a resource hog - it needs its own Docker container which easily can consume half a GB of RAM whilst idle! As noted I only have 4GB on my Pi so I decided against running this for now.
- I may reconsider this after deciding against running qBittorrent on the server, although its container was much less resource demanding than OO.
- I did discover though that Nextcloud does offer editing of Markdown documents as default - like Dropbox Paper which I was a big fan of in a previous workplace.
Future considerations:
- Backup strategy:
- Haven’t fully planned this but intention is to build a second server (also running Ubuntu Server + Docker) to back up important data from the main server. (No intention to backup media, the storage space required would be too much.)
- Install a reverse proxy service to expose the server to the internet on a custom domain.
- Need to approach this with a lot of caution as I am not very knowledgeable in network security and obviously there are risks. According to ChatGPT a good solution is to use Cloudflare.
- Monitoring - I Currently have a Glances dashboard which gives a good overview but may look into a more fully featured monitoring + logging service like Grafana.
- Use an old phone as an always on monitoring screen.
- More storage - 1TB will not last very long - I do have a spare 500GB SSD which I have fitted to help but will need eg an external 4TB HDD sometime. (HDDs are perfectly fine for serving video content.)
- Get this knowledge into a GitHub repo so to rebuild the server can just clone and spin up:
- Directory structure
- Permissions
- Netplan config
- Docker compose file
- Fan control - through Glances I can see that the fan is frequently running over 3000rpm. This is rather high when the CPU temp is around 50 C when idle. Need to look into this and set a better fan curve.