Let me ask you something Do you actually need a home server? And I don’t mean it in a “you don’t need it, you just want it” kind of way. I mean, does it have to physically be in your house? Now the idea of a home server not actually being at home is nothing new Techno Tim has already made a video in which he basically moved three of his servers into a collocation rack at a data center, However, depending on the provider, that might get pricey. Renting two units of rack space in a Telemaxx data center here in Germany will cost you 42 euros a month before tax plus the electricity costs. And that price also only applies if you’re signing up for at least 3 years. On top of that, you’re also gonna need to build a machine yourself, which costs money, and actually drive it to the data center, which costs gas But don’t lose all hope, because you can still set up a home server in the cloud without having to pay an exorbitant amount of money. I’m talking about a humble virtual private server. A lot of you guys (myself included) probably already use a VPS for something like a self-hosted VPN, or hosting a website, or maybe a Matrix server. But what about using it as a fully featured quote unquote home server? Can you realistically use a VPS to host things like Jellyfin, Plex, Navidrome the Arr Suite, Paperless or Immich? Well, that’s what I want to find out today – with the help of today’s sponsor, Brilliant. org Brilliant is where you learn by doing, with thousands of interactive lessons in math, data analysis, programming, and AI. A solid learning platform won’t just inundate you with hours of lectures, but will also make you use the new knowledge right away – and this is exactly what Brilliant is doing. With tons of content hand crafted by a team of teachers, researchers, and professionals from MIT, Caltech, Duke, Microsoft, Google, and so on. Their lessons help you develop your skills through actual problem solving, and not just memorizing. And just like with any skill, consistency is key And Brilliant helps you build real knowledge in minutes a day—with fun lessons you can do whenever you have time – maybe as an alternative to doomscrolling and watching the shorts. Their course “How LLMs work”, for instance, is a great entry point if you want to learn about the tech that powers one of the biggest paradigm shifts in the modern computing world. So go ahead and try Brilliant today! You can get access to a full library of content for 30 days, free of charge, And as my viewer, you also get 20% off the annual premium subscription. So thank you Brilliant for sponsoring today's video, and now, let's get back to our "home" server Sooo what’s the actual point of hosting a media server on a VPS? Wouldn’t it be cheaper to buy a raspberry pi or an old thin client, Add an external hard drive, and just call it a day? Probably! However, there are some situations in which you can’t, or might not want to host your services on physical hardware. Perhaps you’re a digital nomad who wants to enjoy a library of movies and TV shows, and share it with a few friends. Or maybe you’re a college student, who mostly lives in a dorm, but also goes back to their parents during the breaks. Besides, buying new hardware is a commitment A Raspberry Pi 5 kit for example, will set you back around 120 euros. And if you just want to dip your toes into self-hosting, and are not quite sure if you even want to do it long-term, a few euros a month might be a better deal than a hundred euros upfront. Now first of all, let’s address the elephant in the room. If we want to host a media server, we’re gonna need some space. Most inexpensive VPS tiers come with anywhere between 15 and 40 gigs of space, which probably won’t be enough for storing all of your favorite movies and shows. At the same time, prices for servers with something like 320 gigs of space get dangerously close to the price of 2 rack units at a collocation data center. However, there is one option that will give us plenty of space, at a fairly low price. And that is Hetzner’s Storage Boxes These are basically network drives which you can mount as a Samba share, and are commonly used for backups. However, there’s technically nothing keeping us from using it as a media storage for Jellyfin or Plex. The cheapest storage box, BX11, will give us 1 terabyte of storage for 3 euros and 81 cents a month. 1 terabyte is not a lot, but it’s also a lot more than 40 gigabytes, and it should be plenty for a modest media collection. And as for the server itself, since we’re going with Hetzner already, their cheapest offering, CX22, should be plenty enough for our needs. With 2 virtual CPUs, 4 gigs of RAM, as well as a dedicated IPv4 address, for 4 euros and 51 cents a month, it’s a pretty good value. With the 1 terabyte storage box included, this adds up to 8 euros and 32 cents a month. Which, by the way, is cheaper than Netflix’s standard plan. Not to mention any of the other services that could replace the big tech SaaS offerings. But if that’s still a bit too expensive for you
Segment 2 (05:00 - 10:00)
liteserver. nl offers a plan with 1 CPU core, 1 gigabyte of RAM, as well as 512 gigabytes of storage, for 6 euros a month. Though personally, I still think that the Hetzner option is a better value. And yes, the idea of renting a dedicated server instead of a VPS, has crossed my mind as well. However, with prices ranging from 30 to 100 bucks a month, this is a pretty big leap from the 8 euros a month that we’ll be spending on our VPS. So I decided to stick with the cheaper option for now. Obviously, there are some limitations to using a VPS instead of an actual home server, and this is definitely not a one to one replacement. Anything that has to do with high speed data transfer, will obviously not work. Most VPS providers offer a symmetrical Gigabit connection for your server, but unless you have a gigabit Internet connection at home as well, you probably won’t be able to take full advantage of that. You also won’t be able to do any kind of media transcoding in Jellyfin or Plex. As a VPS user, you only get a fraction of a whole CPU when it comes to processing power. And unless you splurge out on a dedicated server, you’ll also get no GPU, dedicated or otherwise. And finally, space. Hetzner’s cheapest storage box tier will give us 1 terabyte of space, which might be enough for some people, but depending on how often you put new media on it, you might find yourself needing to delete the TV shows and movies that you’ve already watched. These three limitations, the transfer speed, the lack of transcoding, and the lack of space, also mean that you probably won’t be watching any 4K HDR Blu-Ray RIPs You’ll pretty much have to stick to FullHD or HD content with a relatively generous amount of compression However, a lot of your guys might be perfectly fine with that, and as long as your client device supports direct play, connection speed is enough for the bitrate of the media, it should work just fine. Last but not least, I want to touch on the topic of procuring the content for your server. Right off the bat, I won’t be downloading any illegal copyighted content in this video. Nor will I be demonstrating how to do it. We will be hosting a torrent client and a couple of programs that can automate downloading media via torrents. However, torrenting, in and of itself, is not illegal, and does not violate any kind of copyright law. There are plenty of media out there that it is public domain, copyleft or otherwise free to use however you want and this is what we’ll be using as an example in this video. So let’s run through the set up that I’m gonna be using in this tutorial As I already mentioned, we’ll be using the cheapest VPS tier from Hetzner, with 2 virtual CPU cores, and 4 gigs of RAM as well as their cheapest storage box, with 1 terabyte of storage. When it comes to how we’re going to be installing and configuring applications on your server, we’re not going to be using NixOS. I don’t expect you to learn an entirely new programming language just to host a couple of services on a VPS. We’re also not gonna be using Ansible, for pretty much the same reason. Instead, we’re gonna stick with good ol’ Debian 12. as well as Docker for running our applications. I’m gonna be using Traefik as a reverse proxy, and DuckDNS to get a free domain name for my services, as well as wildcard SSL certs with DNS-01 challenge. Since our VPS is publicly available, I’m also gonna putting it behind an authentication proxy, in this case, Authelia. You don’t want to end up like one of those people on r/homelab, do you? As for the actual services, I’m gonna be running Jellyfin, Sonarr, Radarr, and Deluge Feel free to extend this list with other self-hosted services if you want. That’s all for the setup, and without further ado, let’s create and configure our server! First things first, we’re gonna need to buy our virtual server, as well as our storage box. And I’m gonna start with the storage box, for reasons that will become apparent later. If you don’t have a Hetzner account yet, you will need to register at console. hetzner. cloud. Keep in mind that depending on your location, or if you’re registerting from behind a VPN, Hetzner might ask for a copy of your ID for the purposes of fraud prevention. If you’re not comfortable with that, feel free to pick another provider, but also keep in mind that a lot of other VPS providers have similar rules in place. After registering an account, you need to go to robot. hetzner. com, since this is where we can buy our storage box. Here, we’re gonna click on Ordering. scroll all the way down, and click on “Storage Boxes BX”. I’m gonna choose the cheapest option, BX11, and click on Order product. For the location, I’m gonna go with Helsinki, and finally, scroll a bit down, click on Checkout, and click on “Order on obligation”. Provisioning up a storage box takes a little bit of time, and while we wait, we’re gonna go ahead and buy the VPS Go to console. hetzner. cloud, and you should see this screen, assuming you’re logged in with your Hetzner account. Now, we can create our first project. I’m gonna name the project “homeserver”, and this name is arbitrary, you can call it whatever you want. Then, we’re gonna click on the project, and then click on “Add Server”.
Segment 3 (10:00 - 15:00)
For location, I’m gonna leave it at Helsinki. Next, for the OS, we’re going with Debian 12, as I already mentioned. Next, we’re gonna select our server type. For the architecure, i’m gonna go with x86, since it provides the most wide compatibility with all of the applications. Feel free to go with the cheapest ARM64 tier, If you feel adventurous – it should cost the same as the cheapest x86 tier, and in theory be compatible with all of our appliations. but do keep in mind that I personally haven’t tested that combination so your mileage may vary. I’m gonna go with CX22, with 4 gigs of ram and a 40 gigs SSD. On the networking tab, we’re gonna keep everything as it is, and then we’re gonna need to add our SSH key. If you already have an SSH key – feel free to use it here instead of generating a new one. But I’m actually gonna generate one now. So I’m gonna open a terminal, go to the. ssh folder inside my home directory and run `ssh-keygen` I’m gonna enter the path to my new key and then i’m gonna enter the password for my new key twice. Keep in mind, the password will not be shown on the screen as you’re typing. And now, we should be able to show our public key on the screen, by typing cat, and then the path to our key with a. pub extension. Then, I’m gonna copy the public key, and paste it into the SSH key field on the Hetzner’s website. Now let’s click on Add SSH key, and continue with our configuration. We’re gonna set up a temporary firewall for our server later, but for now, let’s skip the firewall section, and scroll all the way down to Server Name. I’m once again going to call mine “homeserver”. Finally, we can click on “Create and buy now”. After the server is created, we’re also gonna add a firewall. We want to make sure that all of our services are secured before opening the server to the world, so, we’re going to block access for everyone except for ourselves, kind of like a poor man’s VPN. Speaking of VPNs, I’m gonna connect to my VPN now, and then I’m gonna click on “Create Firewall”. To be clear, you don’t need a VPN for this, and the reason why I use one will become a bit more clear later. Here, I’m gonna set our allowed TCP ports to “any”, and in the IP section, I’m gonna put the public IP address of my VPN endpoint. If you don’t know your public IP, you can go to the terminal, and type `curl ipinfo. io/ip` Now we can copy the IP address from the terminal into the IP field here, and also do the same with the ICMP rule, which will allow us to ping the server. Finally, we’re also gonna create a UDP rule. So let’s paste our IP here, choose UDP as the protocol, and choose the Port “any”. Now let’s scroll down to “apply to”, and choose our newly created server. We’re gonna call our firewallt `homeserver-firewall-temporary`, and then we’re gonna click on “Create Firewall” And that’s it! Now let’s test that our firewall works – and that’s why I’d actually connected to my VPN earlier. So i’m gonna go to the terminal, and try to log in to the server, by typing `ssh root@ip-address-of-the-server -i /path/to/the/ssh_key` and as you can see, after entering the password for our key, we’re able to log in to the server. However, if I disconnect the VPN and try to log in to the server again, as you can see, it doesn’t work. which is exactly what we want to see. So, I’m gonna connect to the VPN again, and log myself into the server First things first, I’m gonna update the system to the newest state by typing `apt update && apt upgrade` Then, I’m gonna install a few new packages, by typing `apt install sudo cifs-utils curl` After that, I’m gonna create a non-root user, in my case `notthebee` For that, I’m gonna type `useradd -m -s /bin/bash -G sudo notthebee` -m is gonna create a home for our user, -s /bin/bash is gonna set their default shell to bash and -G sudo is gonna add them to the sudo group. Finally, we’re gonna create a password for our new user, by typing `passwd name-of-the-user` This is the password that we’re gonna use for sudo, as well as to log in to our server for the first time, to copy the public SSH key. Now that that’s done, we can actually log out and copy the ssh key to the new user, by typing ssh-copy-id -i, path to the key, notthebee, or the name of your user, at ip-address. You’re gonna be asked to enter the password that we just created in the previous step. After that’s done, let’s try to log in with our user and our private key. Make sure this says `Enter passphrase for key` and not user’s password. And there we go! We’re now logged in Next thing we’re gonna do is harden our SSH settings. For that we’re gonna edit the file called /etc/ssh/sshd_config.
Segment 4 (15:00 - 20:00)
I’m gonna use nano this time around, because after the last tutorial, where I used vim, I’ve got a lot of complaints from VPS providers, which had thousands of users trapped inside their servers, Sword Art Online style. We want to avoid that if possible. So in this file, we want to change two settings PasswordAuthentication – set that to no. PermitRootLogin – also You can use Control W to search for those settings. And then presss Control-O to save the file. And Control-X to quit. after that, we can restart the ssh service, by typing `sudo systemctl restart sshd` Now, if you open a new terminal window, and try to log in to the server with root, it will not work. Likewise, if you log in with your non-root user without your private key, and provided you don’t have the ssh agent taking care of your keys, this will also not work. Logging in with the key should still work though. So now that we’ve configured our server, it’s time to mount our Storage Box. By now, you should’ve gotten an email that your storage box is ready, so let’s go to robot. hetzner. com, and take a look at it. Let’s go to Storage Box, and click on our storage box right here. Here, we’re gonna enable Samba support, and also feel free to disable External reachability, if you’re not planning to use this storage box outside of Hetzner’s network. At the same time, if you’re planning to upload the media from your local machine, it might be a good idea to leave it on. Then, you’re gonna want to click on Reset password, and as you can see we’ve got our new password right here. Now, I’m gonna switch back to the terminal and create a new file in my home directory on the server, called dot smb. Here, I’m gonna type user=, password= and domain= and fill them out with the values on the hetzner page. User name for user, password for password and server for domain. Now I’m gonna save the file and quit. Finally, we’re gonna restrict this file’s permisisons, by typing `sudo chmod 600. smb` Next, we’re gonna create a folder that our samba share will mount to so let’s type `sudo mkdir -p /mnt/media` Next, we’re gonna open a file called `/etc/fstab` and define a mount for our samba share, so that it’s mounted automatically when the system boots We’re gonna start by going to the Hetzner page, copying the Samba CIFS share, and pasting it on the new line. Next comes the folder that we’ve created earlier, so /mnt/media. Then we’re gonna write cifs uid=0, credentials=/home/your-user-name/. smb, iocharset=utf8, noperm 0 Finally, let’s save the file, exit the text editor, and test our mount. For that, we’re gonna type `sudo systemctl daemon-reload` followed by `sudo mount -a`. And now, if we run `df -h`, we should see our Samba share right here. Fun fact – In newer versions of Debian, fstab mounts are automatically converted into systemd units, that’s actually why we had to run `systemctl daemon-reload` earlier. The cool thing about that is that systemd automatically detects remote file systems. So if we look at our new mount unit, we’ll see that it has a `remote-fs. target` as its Before value. Next up, we’re gonna install Docker. I’m pretty much gonna follow Docker’s official tutorial for Debian, which consists of copy pasting exactly two times. one and two. After that, I’m also gonna create the docker group. which apparently already exists add my user to it and run `newgrp docker` to avoid logging in and out. Now, if we run `docker run hello-world`, it should just work. Last thing to do before we can finally configure our servers, is get a domain name For this project, I’m gonna use DuckDNS. So I’m gonna go to duckdns. org, log in with my Google account, you can also use Github, and create a new domain name called `oldmanyellsatcloud`. Then, I’m gonna go back to the terminal, open a new tab, log in to the server, and run `curl http://ipinfo. io/ip` to get my public IP address. And now I’m gonna copy and paste it into the IP field, and click on update IP. Now we’re ready to configure our services. In order not to drag this video out even more, I’ve prepared a Github repo with the compose file, So I’m gonna go to the terminal, create a folder at /var/opt, make it belong to my non-root user using chown. and then I’m gonna install git, by typing `sudo apt install git` After that, I’m gonna clone the git repository, and change to the directory with the compose file. First thing we’re gonna do here is copy the env template to. env.
Segment 5 (20:00 - 25:00)
Let’s open that file, and define our DuckDNS domain As well as our DuckDNS token You can see both once you login to DuckDNS. Now let’s save the file and quit. Now let’s open the compose file, and take a look at what services we’re gonna be running. And yes, starting with compose version 2, the recommended file name for a docker compose project is compose. yaml, and not docker-compose dot yaml. So here, I’m defining the traefik container with like two dozens of configuration options. We’re using DNS-01 challenge, because one – this will let us get a valid SSL certificate even though our server is firewalled, And two, it will also let us get a wildcard certificate that will work for all of the subdomains for our services So we won’t need to request a separate certificate for each and every service. Then, I set up a redirect from HTTP to HTTPS, and also define the domain name for the certificates, using the variable that we defined earlier in the dot env file. This part proxies the Traefik WebUI itself, and locks it behind Authelia, which is defined below. I’m also passing through my DuckDNS token right here, which we defined earlier in our env file. Next, Authelia. We need Redis for Authelia, so that’s what I’m defining here. And here I’m also defining a special service that generates random secrets for Authelia. It’s a pretty simple bash script and this is what it looks like. This container runs before Authelia, so that all the secrets are generated before Authelia starts. Finally, we’re gonna define our actual services. This is pretty much in line with the examples provides by linuxserver. io with one exception, and that is using Traefik for reverse proxying and using Authelia for authentication. For most services, we’re also mounting the TV, Movies and downloads folders from our Hetzner storage box, which we’ve mounted to /mnt/media. As for why I’m using Traefik, and not Nginx Proxy Manager. The killer feature of Traefik, at least for me, is that you don’t need to define the proxied services manually in the WebUI If the service that you want to proxy has the right Docker labels, it will automatically be detected by Traefik and proxied. For instance here, I define the subdomain, so sonarr. Then, I define the entry point, which is HTTPS in my case and finally I define a middleware – and this is the part that lets us authenticate visitors with Authelia. As you can see, we have these labels on all of our services, and adding a new service is as simple as copying the traefik labels from one of the services that we’ve already defined, and replacing the subdomain with the one that we want. Traefik will automatically detect the port which needs to be proxied, and if that doesnt’ work for some reason, you can still define it yourself. This makes the configuration more portable, since pretty much everything is contained in the compose file. Finally, if your service already comes with its own authentication, like, for instance, Jellyfin, you can simply remove the middleware part, like I did here. In case of Jellyfin, if you do put it behind Authelia, you won’t be able to access it via client applications, like Infuse. So let’s see if our solution works! I’m gonna close the file, and run `docker compose up -d --build`. And once again, starting with compose v2, there’s no dash between docker and compose. So now that our compose stack is up, let’s look at the traefik output, by typing `docker logs -f traefik`. The first time you start the Traefik container, it’s gonna try to request a certificate from Let’s Encrypt. This is gonna take some time, and you can follow the process by looking at the log. After a few minutes, you should see a message indicating that the certificate verification was succesful, which means it’s time to try it out. So let’s switch to a browser, and go to, let’s say, deluge. domain-name. In my case, oldmanyellsatcloud. duckdns. org If all went well, you should see this screen here. The default credentials are authelia: authelia, and we’re gonna change them later. But after logging in, we should see this – success! Now let’s create a user for Authelia. First things first, we’re gonna need to hash our password. I’m gonna use this command, which you’ll find in the github repository. The only thing we need to do is replace this part with the actual password. Now we can copy the resulting hash, and edit the file called /var/opt/data/authelia/users_database. yml making sure that we use sudo. Here, we’re gonna replace the authelia user with our name and username, replace the password hash with the one that we just generated. and replace the email as well You’re gonna need to repeat the process for any of the additional users that you want to create. After we’ve configured our users, we can now restart authelia, by typing docker restart authelia. Now let’s test our new credentials! If you get this error, this means that you need to wait a few seconds for Traefik to pick up the Authelia configuration. And after opening the page in an incognito window, we can now log in with our new credentials! Authelia allows for some pretty advanced configurations as well. you can use an LDAP database to synchronize your users between
Segment 6 (25:00 - 28:00)
Authelia and other applications that support LDAP you can use an external service to authenticate your users – like Google, Github, Facebook, or any other service that supports Oauth2. And you can also define user groups, and make it so that certain groups only have access to certain docker services, but not the others. However, I’m not gonna cover it in today’s video. I’m also not gonna go deep into configuing Sonarr, Radarr, or any of the other services here. We are, however, gonna set up Jellyfin, and test video playback. So let’s go to jellyfin, and start the initial set up. Here, we need to set the language, and set our username and password – be sure to set a good password here, since as I mentioned before, we won’t be proxying Jellyfin through Authelia. Next, we define our libraries. With our TV library being mounted to /tv and the movies library mounted to /movies. Finally, we confirm the language and leave the remote connections option on. And that’s it! I’ve tested the playback with a FullHD version of big buck bunny, which I uploaded to the server using Samba. And it worked pretty well! Even the 4K version worked on my home internet connection. As I mentioned before though, you can’t use this setup for video transcoding. So if you plan to use it on different kinds of internet connetions, it might make sense to stick to FullHD, since you can’t transcode high bitrate files to a lower bitrate. I’ve tried to do it anyway, just for laughs, by playing back a 60 Megabit version of the test file from Jellyfin’s website. Credit where credit’s due, Jellyfin did try to transcode it, at about 13 FPS, until eventually crashing to the point where I couldn’t even open the WebUI Unfortunately, there’s no way to disable transcoding entirely in Jellyfin though, so depending on your use case, it might make sense to use a client that supports direct playback for a widre variety of files. For instance, when i try to play back the same video file through Infuse, it works like a charm… aaaas soon as the video is done buffering. Finally, it’s time to remove our firewall. we’re gonna test all of our services one last time, just to make sure that every service is protected with some kind of authentication If you try to do that in your browser though, Authelia is going to remember you after the first log in, and not ask for your password again for some time. Which makes testing harder. The easiest way to test the authentication, in my opinion, is to go through every service in your docker compose, and open them one by one in curl. You should get this result for every service that you’re using Authelia for. As you can see, in my case, I got something else for sonarr. So I went to look at the compose file, and sure enough, there was a typo. After fixing it and running `docker compose up -d` though, sonarr is also protected by authelia. Once we’re sure that our services are secure, we can go to Hetzner’s admin panel, and remove our firewall. But the biggest question is – is the cheapest VPS plan from Hetzner enough for running all of your services? Well, as you can see, while downloading three torrents simultaneously in Deluge and watching the 4K version of Big Buck Bunny in Jellyfin, our resource usage still looks pretty good – less than 1 gig of RAM, and a load average of less than one. So if you want to host even more stuff on your cloud home server – like Paperless or Immich – knock yourself out. Most of the self-hosted apps are actually pretty light on resources, and unless you’re planning to do machine learning or similar stuff, you should be more than okay. And if not – Hetzner actually allows you to rescale your server to a higher tier, without any sort of data loss. If you leave the “CPU and RAM only” option on, you’ll be able to downgrade your server again in the future. So that’s it! We’ve set up a barebones media server on a VPS and it actually worked out surprisingly well! Who knows, maybe I was wrong to spend a small fortune on my homelab Anyway, that’s gonna be it for today, and as usual, I would like to thank my patrons