How I set up a home server for self-hosting and as a NAS with secure remote access via Tailscale


updated

I turned my old Dell PC into an all-in-one home server and network attached storage to self-host all my data, my photos, and my media library, running Home Assistant, Plex and other services, all securely accessible from outside my home with Tailscale.

Table of Contents

  1. Server hardware and software
  2. Docker containers
  3. Storage, SMB shares and MergerFS
  4. Tailscale for remote access
  5. Other homelab things

Server hardware and software

My old desktop PC turned server is a Dell XPS 8920 with an Intel i7-7700k CPU and 24 GB of DDR4 RAM. (I removed the AMD RX 480 GPU it came with, since I was not going to use it.) I installed Debian 12 Bookworm on an NVMe drive and added hard drives to every available SATA port.

The large hard drives are pooled together using MergerFS. For secure remote access, I have settled on Tailscale for it’s ease of use and exceptionally good free tier. To manage the server with a nice GUI, I use Cockpit. You can add “applications” for visualizing performance metrics, managing storage, and configuring virtual machines. (Which I rarely use.) I also use add-ons File Sharing to manage my SMB shares and Navigator for a graphical file manager.

Cockpit Overview Cockpit Storage Cockpit File Sharing Cockpit Navigator

Docker containers

Aside from the ones mentioned above, most of my other self-hosted apps and services are run as Docker containers. I’ve run Plex and many other things for years with Docker and I see no reason to stop any time soon. Docker can be installed quickly and with minimal hassle by using their official install script:

curl -fsSL get.docker.com | sudo sh

I’ll devote a section to each docker container I run and include my compose.yaml file for each.

Dozzle

Dozzle is a container log viewer. Portainer shows logs as well, and while it’s useful for “live” logging I find Dozzle’s UX much better for deep analysis of past logs.

File Browser

Filebrowser is exactly what the name implies, a GUI file explorer accessed via web UI. I rarely use it, but I have it setup to serve my /home directory in case I ever need to access it from another device.

Gluetun

Gluetun is a VPN client inside a docker container, it can connect to almost any VPN provider, using either OpenVPN or WireGuard protocols. By hooking up another container’s networking to Gluetun, that other container will connect through the VPN. I use qBittorrent with Gluetun for private torrent downloads, that way I don’t expose my IP address and avoid angry letters from my ISP.

Home Assistant

Home Assistant is a smart home automation hub that provides local control over IoT and smart devices in my house. Although I use Google Home on the regular because it’s easier to just speak what I want to do, everything that I can also connect to Home Assistant, I do. It has let me keep controlling my lights a few times when my internet was out, so that alone makes it worthwhile, and creating “if this then that” automations as useful as it is fun.

Kavita

Kavita is a simple user friendly ebook manager and reader, which I’ve been using to read my last few books on either my phone or tablet. It has a really nice and user friendly web GUI.

Nginx Proxy Manager

Nginx Proxy Manager is nice GUI wrapper over Nginx that lets you easily add proxy hosts and redirects, configure TLS, etc. I use it as a reverse proxy to access container web UIs with HTTPS via a custom domain. I use AdGuard Home as my home network DNS, so I have DNS rewrites configured for all the proxy hosts, and a custom domain from Cloudflare gets TLS certificates via DNS challenge.

For details see this blog post about setting up Nginx Proxy Manager with AdGuard Home and Cloudflare.

OpenGist

OpenGist is a self-hosted open source alternative to GitHub Gists. This is only accessible to me and I use it to save like API keys or tokens, configuration files, and code snippets so I can quickly copy & paste these things when I need to.

Paperless-ngx

Paperless-ngx is a document management system that can index and organize documents, performing OCR to make them searchable and selectable, and saving them as PDFs. If you have a lot of papers you want to digitize and organize, Paperless is a powerful tool for that. You can designate a consume folder and any documents dropped in there will automatically be processed by Paperless.

I honestly don’t use it that much, but my wife and I have fed all our tax returns, property documents, and important receipts to it so that we can just go to one place from any device to view, edit and print documents.

Plex

Plex is a slick, feature packed media server and streaming player for self-hosted media. It also has some free movies and TV shows, and live TV channels. It’s not open source, some features are behind a paid subscripton or lifetime pass, and the company hasn’t always made good decisions for its users — but it’s still the best and most user friendly media player for me, my wife and two family members I have shared with.

I have written blog posts about how to self-host Plex as a Docker container and how to use Tailscale and an Oracle free tier compute instance to securely expose Plex to other users.

Portainer

Portainer is always the first container I install on a server that will run Docker. It is a GUI for creating and managing containers, I use the Stacks feature to create different groups of containers with docker compose. I have copies all the compose files (with secrets removed) saved on GitHub. It’s possible to setup Portainer to pull compose.yaml files from a GitHub repo for setting up Stacks, after which updates in GitHub will be pulled into Portainer, but I have not set this up myself. (I think I just prefer to do it manually in Portainer.)

Thanks to Portainer’s 3 node free license I also use Portainer Agent, connected via Tailscale, to manage another set of remote containers running on an Oracle free tier instance.

Portainer is usually installed with docker run rather than compose, it’s just a quick command to get started. (Note that I use a bind mount rather than a persistent volume for Portainer.)

docker run ...

qBittorrent

qBittorrent is my preferred torrent downloader, this containerized version makes the GUI accessible from any machine via browser, and it connects to Gluetun so that so all my downloads are routed through a paid VPN. Downloads go to my server’s media storage to be streamable on Plex and accessible on the network via SMB shares.

Scrutiny

Scrutiny provides a nice dashboard for hard drive S.M.A.R.T. monitoring. I have 7 hard drives on my server of various manufacturers, storage capacity and age so I use this to keep an eye on all of them. (See first screenshot below.) You can also see details on the test results for each drive and decide how severe it is. (See second screenshot, I’m not too worried since it’s not critical and the content of both drives are backed up anyway.) Of course I have Scrutiny setup to send me notifications via Pushover (see third screenshot), but I also have smartd daemon configured to send mail in the server terminal when the tests show critical HDD failure, this already alerted me once to a dying HDD that I was able to replace without data loss.

Scrutiny all drives overview. Scrutiny details of a specific drive. Scrutiny notifications about specific drive errors via Pushover.

Syncthing

Syncthing is used for only one thing, keeping my Obsidian notes synced across PC, phone and tablet. (Unfortunately, I’ll have to switch to an alternative eventually since Syncthing for Android has been discontinued.)

Uptime Kuma

Uptime Kuma is a robust self-hosted uptime monitor, it can keep track of not just uptime of websites, but also Docker containers running on the host or even remotely. I mainly use it to monitor my containers and send a push notification to my phone (via Pushover) when they go down and come back up, other than that I track the uptime of websites (including this one) and make sure AdGuard Home is available.

Uptime Kuma various monitors Uptime Kuma container monitor

Speedtest Tracker

Speedtest-Tracker lets you schedule Ookla speedtests with cron syntax and uses a database to keep a history of test results with pretty graphs. It can also send notifications when a speedtest is completed or if a threshold is met. I use Pushover for push notifications from Speedtest-Tracker to my phone whenever speed results are below a certain threshold.

Speedtest Tracker dashboard with graphs Speedtest Tracker push notification with Pushover

Tautulli

Tautulli runs alongside Plex to provide monitoring and statistics tracking, so I can see a history of what media my users and I consumed, details on when and what device, whether it was direct play or transcode, etc. It also has programmatic notifications with a lot different triggers. Aside from just keeping a comprehensive history of played media, I use Pushover to send push notifications to my phone when other users are playing something on Plex and if they have any stream errors.

Tautulli push notification with Pushover

Watchtower

Watchtower keeps track of new version of all your other container images, and (depending on your config) will automatically shut containers down, update the images, prune the old images, and then restart it. You can also schedule your updates for specific dates and times, mine only happen on weekdays at 3 AM. Finally, it can send notifications via many providers, but like with everything else I use Pushover to get notified on my phone when any containers have been updated.

Watchtower push notification with Pushover

Storage, SMB shares and MergerFS

The following storage is installed in the server:

The 256 GB NVMe SSD is the boot drive where Debian is installed. The 4 TB and 2 TB HDDs are used exclusively for movies and TV shows, and 1 TBs are storage for everything else. (Photos, music, documents, etc.)

I don’t bother zfs or RAID. This is kind of a cardinal sin in self-hosting, but I just don’t care enough if one of my drives die and I lose a bunch of movies and TV shows — I can re-acquire any that I want, and ignore any that I don’t care about anymore. (As it is I’m already very bad at removing media I already watched and don’t intend to watch again.) The photos, music and documents are backed up on cloud storage, other machines and an additional dedicated 1 TB backup drive, not listed above. That’s my 3-2-1.

I use MergerFS to create two unified mount points: 10 TB of internal HDDs in /opt/media, where it’s all available to stream in Plex. The other 3 TB of external HDDs is general storage at /opt/data.

MergerFS is quick and easy to install and configure. First, check for the latest version on GitHub and download it:

# latest version when I wrote this
wget https://github.com/trapexit/mergerfs/releases/download/2.40.2/mergerfs_2.40.2.debian-bookworm_amd64.deb

Then install it:

dpkg -i mergerfs_2.40.2.debian-bookworm_amd64.deb

Configuration can be done in multiple ways, but my preference is by editing /etc/fstab. Below is the fstab entry I use:

/mnt/media* /opt/media fuse.mergerfs
defaults,allow_other,nonempty,use_ino,moveonenospc=true,dropcacheonclose=true,category.create=mspmfs,fsname=mergerfs 0 0

/mnt/data* /opt/data fuse.mergerfs
defaults,allow_other,nonempty,use_ino,moveonenospc=true,dropcacheonclose=true,category.create=mspmfs,fsname=mergerfs 0 0

Using SMB, I share the files so they can be accessed from any PC, tablet or phone on the network. (Including via Tailscale.) I also have an SMB share to drop in documents for Paperless to consume. File Sharing is managed via the Cockpit GUI, below is my smb.conf file:

[global]
   interfaces = lo enp3s0
   bind interfaces only = yes
   smb ports = 445
   workgroup = WORKGROUP
   server string = Samba %v %h
   netbios name = athena
   security = user

   log file = /var/log/samba/%m.log
   max log size = 50
   printcap name = /dev/null
   load printers = no

   strict allocate = Yes
   allocation roundup size = 4096
   read raw = Yes
   server signing = No
   write raw = Yes
   strict locking = No
   socket options = TCP_NODELAY IPTOS_LOWDELAY SO_RCVBUF=131072 SO_SNDBUF=131072
   min receivefile size = 16384
   use sendfile = Yes
   aio read size = 16384
   aio write size = 16384

[home]
   comment = Home Directory
   path = /home/ariel
   browseable = yes
   writeable = yes
   read only = no
   force user = ariel
   force group = ariel

[media]
   comment = Media Share
   path = /opt/media
   browseable = yes
   writeable = yes
   read only = no
   force user = ariel
   force group = ariel
   force create mode = 0666
   force directory mode = 0777

[data]
   comment = General Share
   path = /opt/data
   browseable = yes
   writeable = yes
   read only = no
   force user = ariel
   force group = ariel
   force create mode = 0666
   force directory mode = 0777

[external]
   comment = External Backup Share
   path = /mnt/external
   browseable = yes
   writeable = yes
   read only = no
   force user = ariel
   force group = ariel
   force create mode = 0666
   force directory mode = 0777

[paperless]
   comment = External Backup Share
   path = /opt/data/paperless/consume
   browseable = yes
   writeable = yes
   read only = no
   force user = ariel
   force group = ariel
   force create mode = 0666
   force directory mode = 0777

SMB shares are available on the network for my wife and I to access from any PC or laptop, even any tablet or phone — I wrote this blog post about how to access SMB shares from Android. On my Windows PC, I have the SMB shares mapped as network drives and mostly manage them through there.

SMB shares from Linux server mapped as network drives in Windows.

Tailscale for remote access

My preferred way of remotely accessing my home network is Tailscale. If you don’t know, Tailscale is a mesh virtual private network (VPN) that uses the WireGuard protocal for encrypted peer-to-peer connections. For details on how it works, see here.

Tailscale not the only remote access solution, and technically it is not self-hosted, it’s just the solution I landed on and ended up loving. Creating a Tailscale account also creates a Tailnet. Any machines that run Tailscale are added to the Tailnet as nodes, which you’ll manage through the web-based admin console.

Tailscale is easy to learn and use, and when setup properly is totally secure without port forwarding or exposing anything to the internet. I wrote a blog post with more details on how to set it up. The easiest way to install on a Linux server is to use the Tailscale install script:

curl -fsSL https://tailscale.com/install.sh | sh
Information

By default using most Tailscale commands requires superuser privileges, i.e. sudo. By using the command sudo tailscale set --operator=$USER, the specified user will then be able to execute Tailscale commands without sudo.

Once installed, Tailscale is run with the following command:

tailscale up

I have both my home server and the separate machine running Pi-Hole as nodes in my Tailnet, along with my phone, tablet and a laptop. The server acts as subnet router so that I can access the entire network via Tailscale, not just the nodes with Tailscale installed. As per the Tailscale documentation on subnets, this is done with the following commands.

First, to enable IP forwarding:

echo 'net.ipv4.ip_forward = 1' | sudo tee -a /etc/sysctl.d/99-tailscale.conf
echo 'net.ipv6.conf.all.forwarding = 1' | sudo tee -a /etc/sysctl.d/99-tailscale.conf
sudo sysctl -p /etc/sysctl.d/99-tailscale.conf
tailsclae

Then to advertise subnet routes:

tailscale up --advertise-routes=192.168.0.0/24

Finally, to make my SMB shares accessible via Tailscale, I use the following command:

tailscale serve --bg --tcp 445 tcp://localhost:445

Now with the Tailscale client installed on my Android phone, and toggling it on as VPN, I can access my home network on the go. I have Pi-Hole running on a Libre Potato that acts as the DNS server for the Tailnet, so I get ariel blocking on the go too.

Other homelab things

Most everything I self-host is on this one server, but I do have some other things going on. I have two free-tier Oracle E.2micro instances that I connect to via Tailscale. One is used to allow secure remote access to Plex by other users, the other runs Pi-Hole as DNS for the entire tailnet, including my phone when I’m not home.

I have a ZimaBoard running a local instance of Pi-Hole for my tailnet, but it’s usually off because I prefer to use the free Oracle VM. (I may flash OpenWRT onto the ZimaBoard and turn it into a Tailscale travel router.) In addition I have two Libre Sweet Potato SBCs that used to run my home instances of Pi-Hole, but are just sitting in a drawer unused for now.

How to securely expose Plex from behind CGNAT using Tailscale and a free Oracle VM

How to use Pi-hole from anywhere with Tailscale