Repurposing my old laptop to a test server running multiple services (ie. JellyFin, Pihole, NextCloud, etc.) using an Ubuntu Server and a brief overview on how to use my homelab and how its structured.
- HomelabProject-v.1
- Project Description
- Table of Contents
- Features
- Homelab Diagram
- Technologies Used
- Getting Started
- Usage
- Challenges
- Future Improvements
- Acknowledgments
- Offline media server (Jellyfin)
- Self-hosted cloud storage accessible to the internet (NextCloud)
- Web ad-blocker (DNS Filtering) (Pi-hole)
- Recursive DNS for added internet security (Unbound)
- Monitor server status (Htop)
- App packager to avoid dependency conflict. Easier to run than VMs. (Docker)
- VPN for encryption when using public network and to access local services remotely (Tailscale)
Server Specs:
- Laptop Model: Dell Inspiron 14 7447
- Processor:
Intel i7-4710HQ (4C | 8T)
- Memory:
8GB DDR3L 1600mhz
- Graphics:
Intel HD 4600 (Integrated), Nvidia GTX 850M (Dedicated)
- Storage:
128GB SSD (OS Drive), 140GB HDD (Storage Drive)
- Processor:
Others:
- Host PC/Laptop
- Cat 5e LAN Cables (For 1 gigabit transfer speeds)
- Huawei Echolife EG8145V5 (Router)
- Ubuntu Server 22.04.3 LTS (Bare Metal)
- Docker
- Htop
- OpenSSH
- Jellyfin
- Pi-hole
- Unbound
- NextCloud
- Tailscale
- MariaDB
- Apache
- WinSCP
- Ubuntu Server
- Docker
- Jellyfin
- Pi-Hole
- Unbound
- Device running Pi-Hole
- Access to SSH
- NextCloud
- Tailscale
- Apache
- WinSCP
- Download Ubuntu Server OS here.
- Create a bootable USB drive for Ubuntu OS (In this case, I used Ventoy).
- Insert bootable USB into the server PC
- Boot into BIOS in the server PC.
- Change boot priority to the Ubuntu USB Drive (You can also boot override if your BIOS has that).
- Follow on-screen instructions for Ubuntu install.
NOTE: Be sure to include OpenSSH during installation to access server remotely.
Htop Setup:
-
Run apt update and apt upgrade in the CLI
sudo apt update && sudo apt upgrade
-
Install htop using the apt command
sudo apt install htop
Docker Setup:
-
Uninstall old docker packages (It is highly recommended to run this even though you're on a fresh OS)
for pkg in docker.io docker-doc docker-compose podman-docker containerd runc; do sudo apt-get remove $pkg; done
-
Install the Docker Repository
# Add Docker's official GPG key: sudo apt-get update sudo apt-get install ca-certificates curl gnupg sudo install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg sudo chmod a+r /etc/apt/keyrings/docker.gpg # Add the repository to Apt sources: echo \ "deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \ "$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | \ sudo tee /etc/apt/sources.list.d/docker.list > /dev/null sudo apt-get update
-
Install Docker Package
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
-
Verify that Docker is installed
sudo docker run hello-world
Jellyfin Setup: (Docker method)
-
Install docker
-
Using the CLI, download the latest docker image for Jellyfin
docker install jellyfin/jellyfin
-
Create cache,config, and media directories as per Jellyfin container requirements:
mkdir /home/kev/jellyfin cd /home/kev/jellyfin mkdir cache config media
-
Auto mounting the internal hard drive with media content
4.1 Run to know the media storage details
lsblk -o NAME,FSTYPE,UUID,MOUNTPOINTS
4.2 Edit the fstab file
sudo nano /etc/fstab
4.3 Input the UUID, Mountpoint, and drive type of the media storage in this format
UUID=<UUID> <PATH_TO_MOUNT> <DRIVE_TYPE> defaults 0 0
In my case:
UUID=35d41abe-a9ae-4f96-9951-a6d406efe569 /home/kev/jellyfin/media ext4 defaults 0 0
4.4 Restart Ubuntu Server to apply changes
sudo reboot
4.5 Check if media storage is mounted properly
lsblk
-
Run Jellyfin
docker run -d \ --name jellyfin \ --user 1000:1000 \ --net=host \ --volume /home/kev/jellyfin/config:/config \ --volume /home/kev/jellyfin/cache:/cache \ --mount type=bind,source=/home/kev/jellyfin/media,target=/media \ --restart=unless-stopped \ jellyfin/jellyfin
Pi-hole and Unbound Setup:
-
Setup a static IP Address for your server
-
Install Pi-hole using this command
curl -sSL https://install.pi-hole.net | bash
-
Follow on-screen instructions
-
Changing the default Pi-hole password
pihole -a -p <insert new password>
-
Verify if Pi-hole is running by using the static IP address that has been set earlier (use your browser).
In my case:
192.168.100.74/admin
We will configure it later, we need to install Unbound first so that we can just configure all of it at once.
-
Install unbound
sudo apt install unbound
-
Create a configuration file for Unbound
sudo nano /etc/unbound/unbound.conf.d/pi-hole.conf
-
Write and save this in the nano editor
server: # If no logfile is specified, syslog is used # logfile: "/var/log/unbound/unbound.log" verbosity: 0 interface: 127.0.0.1 port: 5335 do-ip4: yes do-udp: yes do-tcp: yes # May be set to yes if you have IPv6 connectivity do-ip6: no # You want to leave this to no unless you have *native* IPv6. With 6to4 and # Terredo tunnels your web browser should favor IPv4 for the same reasons prefer-ip6: no # Use this only when you downloaded the list of primary root servers! # If you use the default dns-root-data package, unbound will find it automatically #root-hints: "/var/lib/unbound/root.hints" # Trust glue only if it is within the server's authority harden-glue: yes # Require DNSSEC data for trust-anchored zones, if such data is absent, the zone becomes BOGUS harden-dnssec-stripped: yes # Don't use Capitalization randomization as it known to cause DNSSEC issues sometimes # see https://discourse.pi-hole.net/t/unbound-stubby-or-dnscrypt-proxy/9378 for further details use-caps-for-id: no # Reduce EDNS reassembly buffer size. # IP fragmentation is unreliable on the Internet today, and can cause # transmission failures when large DNS messages are sent via UDP. Even # when fragmentation does work, it may not be secure; it is theoretically # possible to spoof parts of a fragmented DNS message, without easy # detection at the receiving end. Recently, there was an excellent study # >>> Defragmenting DNS - Determining the optimal maximum UDP response size for DNS <<< # by Axel Koolhaas, and Tjeerd Slokker (https://indico.dns-oarc.net/event/36/contributions/776/) # in collaboration with NLnet Labs explored DNS using real world data from the # the RIPE Atlas probes and the researchers suggested different values for # IPv4 and IPv6 and in different scenarios. They advise that servers should # be configured to limit DNS messages sent over UDP to a size that will not # trigger fragmentation on typical network links. DNS servers can switch # from UDP to TCP when a DNS response is too big to fit in this limited # buffer size. This value has also been suggested in DNS Flag Day 2020. edns-buffer-size: 1232 # Perform prefetching of close to expired message cache entries # This only applies to domains that have been frequently queried prefetch: yes # One thread should be sufficient, can be increased on beefy machines. In reality for most users running on small networks or on a single machine, it should be unnecessary to seek performance enhancement by increasing num-threads above 1. num-threads: 1 # Ensure kernel buffer is large enough to not lose messages in traffic spikes so-rcvbuf: 1m # Ensure privacy of local IP ranges private-address: 192.168.0.0/16 private-address: 169.254.0.0/16 private-address: 172.16.0.0/12 private-address: 10.0.0.0/8 private-address: fd00::/8 private-address: fe80::/10
-
Restart unbound
sudo service unbound restart
-
Login to Pi-hole
-
Go to Pi-hole Settings then click the DNS settings
-
Untick all Upstream DNS Servers except for "Custom 1 (IPV4)"
-
Set "Custom 1 (IPV4)" to 127.0.0.1#5335
-
Scroll down to hit save
-
You can choose to set your DNS servers through the main router or you can do it by device. I will be doing it per device for testing purposes.
Tailscale Setup:
-
Login to Tailscale Web (I used my GitHub account.)
-
Install Tailscale on my Ubuntu server.
-
Run this in the CLI
curl -fsSL https://tailscale.com/install.sh | sh
-
Run Tailscale on Ubuntu Server as an Exit node and accept Pi-Hole DNS Server
tailscale up --accept-dns=false --advertise-exit-node
-
Register your devices to Tailscale (see link to follow steps according to your device).
Tailscale using Pi-Hole as DNS Server Setup
- Go to Tailscale home and click DNS to set DNS Server to Pi-Hole.
- Under Global nameservers, add nameserver.
- Click on custom then enter the IPV4 address provided by Tailscale. (Do not put in your local IPV4 address as Tailscale provides it's own IPV4 address.)
- Toggle Override local DNS
Tailscale as a 'commercial' VPN Setup
- Go to the Machines tab
- Click on the Ubuntu Server's settings
- Edit route settings > Toggle "Use as Exit Node".
Nextcloud Setup:
-
Check if you have wget.
which wget
1.1 If you don't have wget, run this:
sudo apt install wget
-
Run this command to download the Nextcloud zip file.
wget https://download.nextcloud.com/server/releases/latest.zip
-
Setup MariaDB and check if it runs
sudo apt install mariadb-server
systemctl status mariadb
-
Run the secure installation script and follow the promts.
sudo mysql_secure_installation
-
Creating the Nextcloud DB. Access the mariadb console first:
sudo mariadb
-
Creating the Database and setting permissions:
CREATE DATABASE nextcloud;
SHOW DATABASES;
GRANT ALL PRIVILEGES ON nextcloud.* TO 'kev'@'localhost' IDENTIFIED BY '*insert your password*';
FLUSH PRIVILEGES;
-
Setting up Apache by first installing it:
sudo apt install apache2
-
Check apache status
systemctl status apache2
-
Enable PHP extensions
sudo phpenmod bcmath gmp imagick intl
-
Check if you have unzip:
which unzip
10.1 If you don't have wget, run this:
sudo apt install unzip
-
Unzip the NextCloud zip file downloaded earlier
unzip latest.zip
-
Remove the zip file
rm latest.zip
-
Move the nextcloud folder and set pemissions
sudo chown -R www-data:www-data nextcloud
sudo mv nextcloud /var/www
-
Disable the Apache default website
sudo a2dissite 000-default.conf
-
Setup the Apache to serve Nextcloud instance. Make a conf file.
sudo nano /etc/apache2/sites-available/nextcloud.conf
-
Add this to the newly made conf file.
<VirtualHost *:80> DocumentRoot "/var/www/nextcloud" ServerName nextcloud <Directory "/var/www/nextcloud"> Options MultiViews FollowSymlinks AllowOverride All Order allow,deny Allow from all </Directory> TransferLog /var/log/apache2/nextcloud.log ErrorLog /var/log/apache2/nextcloud.log </VirtualHost>
-
Enable the site:
sudo a2ensite apache-config-nextcloud.conf
-
Edit the PHP file
sudo nano/etc/php/8.1/apache2/php.ini
-
Replace the following to these values and uncomment certain lines using nano.
memory_limit = 512M upload_max_filesize = 200M max_execution_time = 360 post_max_size = 200M date.timezone = America/Detroit opcache.enable=1 opcache.interned_strings_buffer=8 opcache.max_accelerated_files=10000 opcache.memory_consumption=128 opcache.save_comments=1 opcache.revalidate_freq=1
-
Enable these PHP mods for Apache
sudo a2enmod dir env headers mime rewrite ssl
-
Restart Apache
sudo systemctl restart apache2
-
Login to NextCloud using local ip address.
Click on the image below to open the youtube link
JellyFin Install
- Folder permissions (Changed Jellyfin permissions)
- Jellyfin keeps restarting (Changed the file path of cache and config files during execution)
NextCloud Install
- Can't seem to fix the Apache install (Solutions: https://askubuntu.com/questions/912638/error-module-php7-0-does-not-exist, and changing the port number of lighttpd to 8080)
- Enabling cron automation by appending '*/5 * * * * php -f /var/www/nextcloud/cron.php --define apc.enable_cli=1' to 'sudo crontab -u root -e' or crontab.
- Self-hosted VPN server like wireguard (Needs its own public IP to run, but my network is behind CGNAT, so the only option is to rent a Virtual Machine and run wireguard there).
- NextCloud access through the public internet (Bit of skleptical on this one since my VPN setup is much more secure. Also requires a CGNAT internet, costs will be much higher. Can be done in the future).
- NextCloud AIO (Will be installed in the docker container, much lighter to run, stable, and reliable).
- Run services in docker for easy management and light resources (Did not run some services in docker in this version to learn and to practice troubleshooting)
- Installing a reverse proxy if ever that I intended to run my services on the public internet without the use of VPN.
- A new server build with power efficient parts, more storage, more RAM, and a better processor with Quick Sync for video encoding/decoding in Jellyfin.
- Will run Proxmox in the new build for a much more versatile Hypervisor and run Virtual Machines and containers.
- A router that can run OPNsense and a managed switch for VLANs and better firewall.
- UPS is also a nice to have for sudden power outages and failure.
I would like to thank the following as it helped me build and learn much more about self-hosting and the linux operating system: