Home labs are systems or dedicated deployments made to learn and improve skills, such as being a sysadmin, running various services to improve your Linux skills, or just tinkering around and discovering new things (this is my reason). Whatever your reason may be, this topic is very intriguing and can help you learn a lot. In this blog, I will share what I did and what I learned from this.
For this implementation I don't want an overkill enterprise-grade implementation that no one can replicate, what I want is a simple implementation that can be copied and used for simple tasks. I am doing this to learn about the different tech involved for a better and more powerful future build for my localized AI training and research use case.
In this implementation, I want to:
Grafana
& Prometheus
).Portainer
) for on-the-go deployment.Jellyfin
).PhotoPrism
).
As you can see I only have 2 backup options which are SMB and PhotoPrism, I know there are several great backup options for me to deploy but as of now these 2 are more than enough and I have other ways of automating my backup tasks.
I am using an HP EliteDesk 800 G2 DM 35W with Intel Core I5 6500T and 16GB of RAM with a Geekbench single-core score of 1083 and multi-core score of 2898. It's not that powerful but powerful enough for my current goals. I bought this refurbished so I was able to get this for dirt cheap.
For this implementation I used the Ubuntu server 24.04 LTS image, you can use the Windows server image or Red Hat instead. If you have never installed a Ubuntu server instance before you can check this great setup guild link. Once you have installed Ubuntu the next step will be installing docker, make sure you do NOT install docker from snap and instead install docker from the official docker repo, as Portainer has issues with the snap docker (no clue why).
I don't have any specific reason to run even the docker installation on my own there are specialized OS for such deployments such as Unraid, truenas, etc. But all I want to do is test, I don't even have a raid setup in this system as this is probably an extra just-in-case copy of my data.
If you are replicating this then it would be wise to have 3 storage drives installed and have at least a RAID 1 configuration for the least possible guarantee of data security.
Docker install steps:
sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
# Add the repository to Apt sources:
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] 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
After the above command is completed execution you can run this command to install docker:
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
Portainer will be our interface for managing docker deployments and also make it easy and intuitive for deploying new stacks of services.
Step 1: Create volume
docker volume create portainer_data
Step 2: Run the deploy command
docker run -d \
-p 8000:8000 \
-p 9443:9443 \
--name portainer \
--restart=always \
-v /var/run/docker.sock:/var/run/docker.sock \
-v portainer_data:/data \
portainer/portainer-ce:2.21.4
9443 and 8000 both can be used to interface locally with Portainer it won’t matter much as we will be using tunneling for the final public interfacing.
SMB is for my large file backup storage, I will be accessing this only on my local network and not allowing it to be accessed publicly over the internet.
sudo apt update && sudo apt install samba -y
sudo mkdir /mnt/sharefolder && sudo chmod 777 /mnt/sharefolder
sudo nano /etc/samba/smb.conf
[smbshare]
path = /mnt/sharefolder
browseable = yes
writable = no
valid users = <<USERNAME>>
sudo smbpasswd -a <<username>>
sudo smbpasswd -e <<username>>
sudo systemctl restart smb
sudo systemctl enable smb
Cloudflare zerotrust is a great way of running private services to the public internet without any issues or hassles with port-forwarding unless you running SMB or a VPN client which I was not able to get to work, regardless this system can allow you to route all your traffic in a very easy way. I won’t go into much detail about the initial setup and all for zero-trust you can check NetworkChuck’s videos for that: Link I installed the Cloudflare tunneling software via the dpkg package instead of a docker instance as it did not work with a docker instance (have no clue why? it’s probably me). The command I used:
curl -L --output cloudflared.deb https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb &&
sudo dpkg -i cloudflared.deb &&
sudo cloudflared service install <<USE THE TOKEN PROVIDED BY CLOUDFLARE>>
Cloudflare tunnel utilized port 7844 which is important for firewall implementation.
For my implementation I have taken a more simple and easy approach to firewall configuration, these are my firewall rules:
sudo ufw allow from 198.41.192.0/24 to any port 7844 proto tcp
sudo ufw allow from 198.41.192.0/24 to any port 7844 proto udp
sudo ufw allow from 2606:4700:a0::/32 to any port 7844 proto tcp
sudo ufw allow from 2606:4700:a0::/32 to any port 7844 proto udp
sudo ufw allow from 198.41.200.0/24 to any port 7844 proto tcp
sudo ufw allow from 198.41.200.0/24 to any port 7844 proto udp
sudo ufw allow from 2606:4700:a8::/32 to any port 7844 proto tcp
sudo ufw allow from 2606:4700:a8::/32 to any port 7844 proto udp
sudo ufw allow from 192.168.0.0/24 to any port 22 proto tcp
sudo ufw allow from 192.168.0.0/24 to any port 445 proto tcp
sudo ufw allow from 192.168.1.0/24 to any port 22 proto tcp
sudo ufw allow from 192.168.1.0/24 to any port 445 proto tcp
sudo ufw enable
Now check all the connections and let’s get to the implementation of other services.
For this to be possible you need to create a folder with all the local bind folders created for the docker instances to store data in, mine is like this:
├── jelly
│ ├── config
│ ├── movies
│ ├── music
│ └── videos
├── photoprism
│ ├── database
│ ├── pictures
│ └── storage
└── prometheus
└── prometheus.yml
Notice Grafana does not have any setup folder, that's because I have a volume defined for Grafana, and it’s just a dashboard to display vitals while Prometheus collects the data.
The folders movies and videos are just for namesake you can use or leave it’s up to you, I won’t be explaining post-install steps as there are plenty of videos explaining that but I am showing the configs you can use to easily install it within Portainer.
The config I used:
services:
jellyfin:
image: linuxserver/jellyfin:latest
container_name: jellyfin
ports:
- "1900:1900"
- "7359:7359"
- "8096:8096"
volumes:
- "/home/deployment/jelly/config:/config"
- "/home/deployment/jelly/videos:/data/Videos"
- "/home/deployment/jelly/movies:/data/Movies"
- "/home/deployment/jelly/music:/data/Music"
environment:
- PUID=1000
- PGID=1000
- TZ=Asia/Kolkata # Replace with your timezone restart: unless-stopped
The 8096 port is the main interfacing port which is important for later while creating tunnels.
PhotoPrism is a great media backup tool for Android and IOS alike and I found it interesting and wanted to try it out. Mainly because it’s free, local, great at indexing and I can re-download it on any device I want, great tool and extremely easy to set up. The following is the stack I used to implement this system.
services:
photoprism:
image: photoprism/photoprism:latest
stop_grace_period: 10s
depends_on:
- mariadb
security_opt:
- seccomp:unconfined
- apparmor:unconfined
ports:
- "2342:2342"
environment:
PHOTOPRISM_ADMIN_USER: "USERNAME" # ENTER ADMIN USERNAME
PHOTOPRISM_ADMIN_PASSWORD: "PASSWORD" # ENTER ADMIN PASSWORD
PHOTOPRISM_AUTH_MODE: "password"
PHOTOPRISM_SITE_URL: "http://localhost:2342/"
PHOTOPRISM_DISABLE_TLS: "false"
PHOTOPRISM_DEFAULT_TLS: "true"
PHOTOPRISM_ORIGINALS_LIMIT: 5000
PHOTOPRISM_HTTP_COMPRESSION: "gzip"
PHOTOPRISM_LOG_LEVEL: "info"
PHOTOPRISM_READONLY: "false"
PHOTOPRISM_EXPERIMENTAL: "false"
PHOTOPRISM_DISABLE_CHOWN: "false"
PHOTOPRISM_DISABLE_WEBDAV: "false"
PHOTOPRISM_DISABLE_SETTINGS: "false"
PHOTOPRISM_DISABLE_TENSORFLOW: "false"
PHOTOPRISM_DISABLE_FACES: "false"
PHOTOPRISM_DISABLE_CLASSIFICATION: "false"
PHOTOPRISM_DISABLE_VECTORS: "false"
PHOTOPRISM_DISABLE_RAW: "false"
PHOTOPRISM_RAW_PRESETS: "false"
PHOTOPRISM_SIDECAR_YAML: "true"
PHOTOPRISM_BACKUP_ALBUMS: "true"
PHOTOPRISM_BACKUP_DATABASE: "true"
PHOTOPRISM_BACKUP_SCHEDULE: "daily"
PHOTOPRISM_INDEX_SCHEDULE: ""
PHOTOPRISM_AUTO_INDEX: 300
PHOTOPRISM_AUTO_IMPORT: -1
PHOTOPRISM_DETECT_NSFW: "false"
PHOTOPRISM_UPLOAD_NSFW: "true"
PHOTOPRISM_DATABASE_DRIVER: "mysql"
PHOTOPRISM_DATABASE_SERVER: "mariadb:3306"
PHOTOPRISM_DATABASE_NAME: "photoprism"
PHOTOPRISM_DATABASE_USER: "photoprism"
PHOTOPRISM_DATABASE_PASSWORD: "PASSWORD" # ENTER DB PASSWORD
PHOTOPRISM_SITE_CAPTION: "AI-Powered Photos App"
PHOTOPRISM_SITE_DESCRIPTION: ""
PHOTOPRISM_SITE_AUTHOR: ""
working_dir: "/photoprism"
volumes:
- "/home/deployment/photoprism/pictures:/photoprism/originals"
- "/home/deployment/photoprism/storage:/photoprism/storage"
mariadb:
image: mariadb:11
restart: unless-stopped
stop_grace_period: 5s
security_opt:
- seccomp:unconfined
- apparmor:unconfined
command: --innodb-buffer-pool-size=512M --transaction-isolation=READ-COMMITTED --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --max-connections=512 --innodb-rollback-on-timeout=OFF --innodb-lock-wait-timeout=120
volumes:
- "/home/deployment/photoprism/database:/var/lib/mysql"
environment:
MARIADB_AUTO_UPGRADE: "1"
MARIADB_INITDB_SKIP_TZINFO: "1"
MARIADB_DATABASE: "photoprism"
MARIADB_USER: "photoprism"
MARIADB_PASSWORD: "PASSWORD" # ENTER DB ROOT PASSWORD
MARIADB_ROOT_PASSWORD: "PASSWORD" # ENTER DB PASSWORD
watchtower:
restart: unless-stopped
image: containrrr/watchtower
profiles: ["update"]
environment:
WATCHTOWER_CLEANUP: "true"
WATCHTOWER_POLL_INTERVAL: 7200
volumes:
- "/var/run/docker.sock:/var/run/docker.sock"
- "~/.docker/config.json:/config.json"
Also, port 2342 is important for the Cloudflare tunnel deployment.
I tried other services such as NetData but the lack of customization and complexity and not being able to use it with my tunneling system was a big downvote for me and that's why Prometheus and Grafana were a great choice. I will dig deeper into the customization in the dashboard I went with but as of now I will explain the collection agents I used:
volumes:
prometheus-data:
driver: local
grafana-data:
driver: local
services:
prometheus:
image: prom/prometheus:latest
container_name: prometheus
ports:
- "9090:9090"
volumes:
- "/home/deployment/prometheus:/config"
- "prometheus-data:/prometheus"
restart: unless-stopped
command:
- "--config.file=/config/prometheus.yml"
grafana:
image: grafana/grafana:latest
container_name: grafana
ports:
- "3000:3000"
volumes:
- "grafana-data:/var/lib/grafana"
restart: unless-stopped
node_exporter:
image: quay.io/prometheus/node-exporter:v1.8.2
container_name: node_exporter
command: "--path.rootfs=/host"
pid: host
restart: unless-stopped
volumes:
- "/:/host:ro,rslave"
cadvisor:
image: gcr.io/cadvisor/cadvisor:v0.51.0
container_name: cadvisor
ports:
- 8080:8080
volumes:
- "/:/rootfs:ro"
- "/run:/run:ro"
- "/sys:/sys:ro"
- "/var/lib/docker/:/var/lib/docker:ro"
- "/dev/disk/:/dev/disk:ro"
devices:
- /dev/kmsg
privileged: true
restart: unless-stopped
Once both of the systems are running go to the Grafana link and set Prometheus as a data source for that use http://prometheus:9090/
for dashboards I used Node Exporter Full
and Cadvisor Exporter
, both of them have great layouts and give great visualization.
The final stack for all my deployments can be found on my gist link. Copy and modify it as much as you want.
Now once we have all the deployments done we have one local IP of our machine let’s consider it as 192.168.1.75 and it as 5 ports to be mapped with Cloudflare, once you are done with all the pre-requisites make sure you follow the following in case you’re running the same set of services as mine:
SSH has a few more steps, which involve creating an application instance within Cloudflare zerotrust, To open the Access > Applications tab, now select the self-hosted option, then configure the new application with a name of your choice and point it to the same subdomain as the main SSH service is pointing. Now you can create custom rules for how you want the SSH service to be accessed, I chose the browser rendering option which can be found in the Additional settings menu.
For my main backups, I am using Cryptomator to create an encrypted version of my important folders which contain all my credentials and personal information, once my encryption is completed my script automatically copies the files to the appropriate folders which later get synced with my proton drive, and my external SSD, for the automation I am using Microsoft Power Automate. As of now, I am using robocopy a Windows utility for incremental file and folder copying and as I have dedicated large-volume storage I can copy a large amount of data from my main system to this dedicated connection via SMB.
This is the robocopy script I am using:
@echo off
set LOGFILE=D:B\backup.log
set ROBOCOPY_OPTIONS=/E /MIR /XD "node_modules" /XF "*.tmp" "*.log" /Z /MT:16 /R:2 /W:1 /LOG:%LOGFILE% /TEE
:: Start the backup process for each folder
robocopy "D:\A" "Z:\A" %ROBOCOPY_OPTIONS%
robocopy "D:\B" "Z:\B" %ROBOCOPY_OPTIONS%
robocopy "D:\C" "Z:\C" %ROBOCOPY_OPTIONS%
robocopy "D:\D" "Z:\D" %ROBOCOPY_OPTIONS%
robocopy "D:\E" "Z:\E" %ROBOCOPY_OPTIONS%
robocopy "D:\F" "Z:\F" %ROBOCOPY_OPTIONS%
robocopy "D:\G" "Z:\G" %ROBOCOPY_OPTIONS%
robocopy "D:\H" "Z:\H" %ROBOCOPY_OPTIONS%
robocopy "D:\I" "Z:\I" %ROBOCOPY_OPTIONS%
echo Backup completed successfully. Log file: %LOGFILE%
Change this to match your needs.
As of now since I am testing I am planning to buy a TPU unit and check AI-related tasks and techniques, once I have everything planned I am planning to increase the scale of implementation and build a more robust and more powerful lab setup.
This was more of a knowledge dump kind of blog which can help me learn what I had done before to make my setup work if any of the above information was of use to you then feel free to use the information as you please and if you are curious about anything just ping me on LinkedIn or Twitter I will try my best to give you a clear response.