I recently moved into a bigger condo which had a wall mount pre-installed in the bedroom for a TV, so I decided to take advantage of it and bought a second TV.
Previously, I was using an Intel NUC attached to my TV and running LibreElec. If you’ve not heard of LibreElec, it’s a very cool minimal Linux OS that sets itself up to run Kodi. It is remarkably easy to set up, and even found and setup my obscure USB remote control automatically.
However, with my second TV that setup wasn’t going to cut it, so I decided to repurpose the NUC as a home media server.
Move the Intel NUC into my closet with the router and run it as a headless server.
Stream content to each TV, as well as any phone or tablet via WiFi.
Have the ability to download additional content via torrents and newsgroups.
The content should be accessible via a Samba share so computers on my network can read/write it.
Customizing the NUC
The Intel NUC I purchased has a M.2 slot for a SSD, which I highly recommend as a boot drive. If you buy a NUC, be careful NOT to get the one that has 16GB of Optane “Memory.” That is not actual RAM like you might expect and it eats up your M.2 SSD slot. I recommend a Samsung SSD.
It does not come with RAM. You probably want 8GB but 16GB would be nicer.
You will want a second hard drive for your media in the NUC’s 2.5” slot. My current build has a 2TB drive but if I was building one today I’d throw in a 5TB 2.5” Drive. I am not a huge data hoarder so 2TB has lasted me a while but your mileage may vary.
Installing the OS and Docker
For my build, I used Ubuntu 64-Bit but honestly in this setup we’re going to use docker for everything so as long as it can install docker and docker compose easily, you’ll be good to go.
You’ll want to attach a keyboard, mouse and monitor until it’s all working.
Create a USB stick and boot into it on your NUC and do the basic install. You can pretty much use the defaults for everything.
Once everything is installed, we can install docker following the standard instructions.
The last thing you’ll want to do is format and mount the second hard drive. If you installed a Desktop linux this is pretty straightforward. In the examples below I’m going to assume you mounted the drive at
One thing I’d recommend doing to make your life a lot easier is to set really global permissions on the
/storage hard drive:
sudo chown nobody:nogroup /storage sudo chmod 0777 /storage
To be clear, this means anyone can read/write the files there. This is a huge security compromise, and I am comfortable with it because I am only storing media on the drive and not anything of importance. I can trust anyone with access to my local network to read and write files in that drive because it’ll just be movie files. Make sure this is safely behind your router and not visible to the external world.
The Magic of Docker Compose
Here’s where things get really fun. In the past, I’ve found setting up and installing server applications somewhat difficult. For example, sonarr uses Mono. I’m a developer but I’ve never used .NET and have no idea how to configure and set up a server in that framework. Usually you can follow the instructions in the README, but debugging snags can be difficult when you don’t know what you’re doing.
Additionally, you have to familiarize yourself with the init/startup scripts for every OS to make sure each server application boots up properly and will restart in the event of a crash.
Finally, updating each server application has a slightly different process. Some are one click via the web app, but others involve package managers or even recompiling code depending on how you installed them.
Docker Compose solves all these problems, and it does it with a remarkably small amount of configuration.
Here’s a simple example which installs sonarr and nzbget:
--- version: "3" services: sonarr: image: linuxserver/sonarr container_name: sonarr depends_on: - nzbget environment: - PUID=1000 - PGID=1001 - TZ=America/Toronto - UMASK_SET=022 #optional volumes: - /docker/sonarr/config:/config - /storage/tv:/tv - /docker/nzbget/downloads:/downloads ports: - 8989:8989 restart: always nzbget: image: linuxserver/nzbget container_name: nzbget environment: - PUID=1000 - PGID=1001 - TZ=America/Toronto volumes: - /docker/nzbget/config:/config - /docker/nzbget/downloads:/downloads ports: - 6789:6789 restart: always
A few notes:
I installed all my docker stuff in
/dockerbut you can put it anywhere you like. Make sure to create the
docker/nzbgetetc directories before you start it up so that the applications have a place to write their files. Note when setting up
volumesthat the paths on the left hand side of the colon are on your local linux box and can be changed. The right hand side is where they’ll be visible to the server application.
sonarrhas an attribute,
nzbget. This means that the nzbget application will start up before sonarr, which is nice because sonarr needs a program to download from newsgroups.
restart: alwaysmeans that the server applications will be restarted if they crash. They’ll also restart automatically if you reboot your computer.
The images in this docker compose configuration come from linuxserver.io, which I’ve found to be reliable and trustworthy. You should find images that suit your purposes and that you trust.
To install everything, go into the same folder as your
docker-compose.yml and type
docker compose up -d. It’ll download the images and start everything. You can then visit
http://your-server-name:8989 and start setting things up to your preferences.
How easy is that?
Adding more applications
In order to stream content to my TVs, I use Plex, which can be installed just as easily in the same configuration:
plex: image: linuxserver/plex container_name: plex network_mode: host environment: - PUID=1000 - PGID=1001 - VERSION=docker volumes: - /docker/plex/config:/config - /storage/tv:/data/tvshows - /storage/movies:/data/movies - /docker/plex/transcode:/transcode restart: always
Another nice application is qBittorrent:
qbittorrent: image: linuxserver/qbittorrent container_name: qbittorrent environment: - PUID=1000 - PGID=1001 - TZ=America/Toronto - UMASK_SET=022 - WEBUI_PORT=8080 volumes: - /docker/qbittorrent/config:/config - /docker/qbittorrent/downloads:/downloads - /storage/tv:/tv - /storage/movies:/movies ports: - 31321:31321 - 31321:31321/udp - 8080:8080 restart: always
In this case, I have set up port 31321 with port forwarding on my router to make certain torrent services happy. You can choose any port you want there and set it up in qBittorent accordingly.
Setting up a Samba Share
If you want to set up a public samba share, I found a great little image for that too! Add the following to your
samba: image: jenserat/samba-publicshare tty: true environment: - PUID=1000 - PGID=1001 ports: - 445:445 - 137:137 - 138:138 - 139:139 volumes: - /storage:/srv
Now you’re off to the races, any computer on the network can access storage to add/remove files as they like.
Remebering all those ports
If you’re like me, you’ll have trouble remembering all the custom port numbers for your web applications. I decided to install nginx on port 80 so that it would be the default site when I accessed the default port of my media server. On that site I added a simple HTML index page that links to the various services:
nginx: image: linuxserver/nginx container_name: nginx environment: - PUID=1000 - PGID=1001 - TZ=America/Toronto volumes: - /docker/nginx/config:/config ports: - 80:80 - 443:443 restart: always
My index.html looks like this (my server is named
<html> <head> <title>datacube</title> </head> <body> <div class="message"> <h1>datacube</h1> <ul> <li><a href="http://datacube.local:8080/">qBittorrent</a></li> <li><a href="http://datacube.local:6789/">NZBGet</a></li> <li><a href="http://datacube.local:8989/">Sonarr</a></li> <li><a href="http://datacube.local:32400/">Plex</a></li> </ul> </div> </body> </html>
In the end I was blown away about how easy Docker Compose makes setting up all these various services, and keeping them running despite crashes and reboots. If you’re looking to custom build your own media server or download box I highly recommend this approach.