Build Your Own Spotify with Linux

mr.smashy
Nerd For Tech
Published in
5 min readMar 14, 2021

--

I’m leaving the house more and I want to bring my music with me, without ads or tracking.

Jellyfin has provided me with a good replacement for music streaming services

Why do this?

I actually don’t use Spotify, I have used Apple Music for my streaming music needs. I was an iPhone user, jumped ship, but still subscribed to Apple Music because of all the playlists I had accumulated. This summer as a project I built a Jellyfin server with some spare parts I had (a Raspberry Pi 3B+ and a USB 1 TB laptop hard drive) and it was pretty amazing. This is not the ideal setup, the 3B+ lacks Gig ethernet, USB 3.0, and the proc and memory to handle streaming video effectively, although I did watch a lot of video. It did, however, handle streaming my extensive music library without issues. I used to DJ, and collected music. I have a lot of albums ripped from vinyl. My digital music collection is around 1.1k albums. I wanted access to this over the Internet, so I wouldn’t need to pay for streaming music. Also what I listened to would be between me and my server, no third parties.

The Plan

I couldn’t keep using the 3B+, it was time to get a Pi 4B. I also needed to add a proxy, a certificate, and a dynamic DNS name. I ordered a Pi 4B, a Flirc case, and a 2 TB 3.5" drive with a USB 3.0 external enclosure. I had already setup dynamic DNS with several providers, and I suggest you do the same. I added a subdomain for this project to my Dynu DDNS configuration. I flashed a Micro SD with Rasbian Lite and configured a spare 3B+ for Nginx. Once the Pi 4B arrived I installed it in the Flirc case and migrated all my data from my old Jellyfin server to the new HDD and installed Jellyfin on the Pi 4B. The Jellyfin docs say active cooling is recommended for the Pi 4B, but they reference Jeff Geerling, and he recommends the Flirc case for most uses.

The Flirc case is pretty.

Putting it All Together

I found this great guide to installing Jellyfin on the Pi 4B here on Reddit. Once I had tested my Jellyfin server locally, I had to configure my Ngnix proxy. After forwarding 80 and 443 from my router to my Nginx proxy, I needed a cert, so I installed docker and docker-compose and used SWAG. I just needed the cert, so I configured the docker-compose.yml and ran docker-compse up and watched the output. Once I had the cert, I stopped the container. The cert is good for three months, so I’ll have to stop Nginx and run the docker again to renew it. Then I needed a proxy configuration. The one from the Jellyfin docs didn’t work for me, so I used this one I found researching my problem.

server {
server_name TV.EXAMPLE.COM;
listen 443 ssl http2; # managed by Certbot

access_log /var/log/nginx/TV.EXAMPLE.COM.access.log;
error_log /var/log/nginx/TV.EXAMPLE.COM.error.log;

ssl_certificate /etc/letsencrypt/live/TV.EXAMPLE.COM/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/TV.EXAMPLE.COM/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
ssl_trusted_certificate /etc/letsencrypt/live/TV.EXAMPLE.COM/chain.pem;
ssl_stapling on;
ssl_stapling_verify on;
add_header Strict-Transport-Security "max-age=31536000" always;
# Security / XSS Mitigation Headers
add_header X-Frame-Options "SAMEORIGIN";
add_header X-XSS-Protection "1; mode=block";
add_header X-Content-Type-Options "nosniff";
# Content Security Policy
# See: https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
# Enforces https content and restricts JS/CSS to origin
# External Javascript (such as cast_sender.js for Chromecast or YouTube embed JS for external trailers) must be whitelisted.
add_header Content-Security-Policy "default-src https: data: blob:; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' https://www.gstatic.com/cv/js/sender/v1/cast_sender.js https://www.youtube.com/iframe_api https://s.ytimg.com; worker-src 'self' blob:; connect-src 'self'; object-src 'none'; frame-ancestors 'self'";

location ~ /.well-known {
allow all;
}
location / {
# Proxy main Jellyfin traffic
proxy_pass http://192.168.1.54:8096/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Protocol $scheme;
proxy_set_header X-Forwarded-Host $http_host;
# Disable buffering when the nginx proxy gets very resource heavy upon streaming
proxy_buffering off;
}
location /socket {
# Proxy Jellyfin Websockets traffic
proxy_pass http://192.168.1.54:8096/socket;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Protocol $scheme;
proxy_set_header X-Forwarded-Host $http_host;
}
}
server {
if ($host = TV.EXAMPLE.COM) {
return 301 https://$host$request_uri;
} # managed by Certbot
listen 80;
server_name TV.EXAMPLE.COM;

return 404; # managed by Certbot
}

I needed an options-ssl-nginx.conf, so I found an example here. I modified it slightly by removing TLSv1 from ssl_protocols. I started the Nginx service and everything worked. I could view my Jellyfin server via the DDNS URL I had configured. I tested my SSL config with the digicert test page and everything tested good. I reconfigured Gelli on my phone to use the DDNS URL and a login I had created on my Jellyfin server that was allowed to login externally. I was able to login and see my music on 4G LTE. So I hit play, and it worked.

Did This Pay Off?

Since then I’ve used Gelli on my phone during walks and train rides downtown to the office. I’ve logged into my server from a work web browser in the office. It’s been extremely successful and I cancelled my Apple Music subscription and uninstalled the app. All my favorite music is always available on my phone, and I have hundreds of Gigabytes of space available for new music. The Pi 4B has also performed wonderfully, both with transcoding and thermally. This has paid off. It’s not an easy project, but if you have a music collection, I would recommend it. Watching your favorite old anime on your TV is pretty awesome too.

Core Temp of Pi 4B over 14 Days

--

--

mr.smashy
Nerd For Tech

Cybersecurity architect. Security dev and researcher. Infosec nerd. Linux enthusiast. All opinions and views are my own. Polite, professional, prepared.