[{"content":"Welcome to my Infrastructure Blog.\nThis site is dedicated to documenting homelab builds, automation scripts, and technical research, with a heavy focus on Linux, enterprise networking, Infrastructure as Code, and personal infrastructure projects.\nWrite-ups and project documentation are actively ongoing as I archive completed builds and explore new ones.\n","date":"19 May 2026","externalUrl":null,"permalink":"/","section":"","summary":"","title":"","type":"page"},{"content":"","date":"19 May 2026","externalUrl":null,"permalink":"/projects/","section":"Projects","summary":"","title":"Projects","type":"projects"},{"content":"","date":"17 May 2026","externalUrl":null,"permalink":"/tags/file-systems/","section":"Tags","summary":"","title":"File Systems","type":"tags"},{"content":"","date":"17 May 2026","externalUrl":null,"permalink":"/series/home-media-server/","section":"Series","summary":"","title":"Home Media Server","type":"series"},{"content":"","date":"17 May 2026","externalUrl":null,"permalink":"/tags/infrastructure/","section":"Tags","summary":"","title":"Infrastructure","type":"tags"},{"content":"","date":"17 May 2026","externalUrl":null,"permalink":"/tags/linux/","section":"Tags","summary":"","title":"Linux","type":"tags"},{"content":"","date":"17 May 2026","externalUrl":null,"permalink":"/tags/raid/","section":"Tags","summary":"","title":"RAID","type":"tags"},{"content":"","date":"17 May 2026","externalUrl":null,"permalink":"/series/","section":"Series","summary":"","title":"Series","type":"series"},{"content":" Storage Management \u0026amp; Pooling # When it came to choosing the current storage software/tool for this media server I had a few requirements that I wanted the setup to :\nPool all drive under a single directory Ability to expend the drive pool with more drives later Avoid restricting the drive to this pool/system only, without the need to wipe it. (think hardware raid) Support mismatch of drive size and types Drive redundancy support (raid like) ZFS? # ZFS was one of the first options I wanted to see if I could use, but back then it did not allow for expending the pool after its first initial release. Which didn\u0026rsquo;t work for me as I was not sure I wanted to limit the pool to a size and stick to it, I wasn\u0026rsquo;t sure how much storage I would want at the end.\nCombined with the high usage of RAM ZFS requires I decided to look in other directions for my pooling needs.\nLVM # LVM was a tool I was familiar with and has hit most of my needs when it came for a pooling storage tool, adding drives to the pool would be an easy task but removing them, as well as deciding where the actual files landed was not an feature that LVM had. Which lead me to look at other options, but LVM was a strong contender.\nMergerFS \u0026amp; SnapRAID # MergerFs has checked all my requirements but the drive redundancy capabilities, this is where snapRAID a tool that is often recommended along side mergerFS due to the two tools complimenting each other.\nMergerFS is a union file system that sits on the file system level, which allows each drive to be configured with it own file system, as well as does not add anything to the drive to restrict them from being plucked out of the pool and connected to another system.\nWhen combined with snapRAID which allows for raid like drive redundancy to help with physical drive failures. While still allowing the pool to be flexible and redundant. While keeping to the KISS principles.\n","date":"17 May 2026","externalUrl":null,"permalink":"/projects/home-media-server/storage--directory-structure/","section":"Projects","summary":"","title":"Storage","type":"projects"},{"content":"","date":"17 May 2026","externalUrl":null,"permalink":"/tags/storage/","section":"Tags","summary":"","title":"Storage","type":"tags"},{"content":"","date":"17 May 2026","externalUrl":null,"permalink":"/tags/","section":"Tags","summary":"","title":"Tags","type":"tags"},{"content":"","date":"14 May 2026","externalUrl":null,"permalink":"/tags/containers/","section":"Tags","summary":"","title":"Containers","type":"tags"},{"content":"","date":"14 May 2026","externalUrl":null,"permalink":"/tags/docker/","section":"Tags","summary":"","title":"Docker","type":"tags"},{"content":" Overview # When choosing the hardware for my home media server, I decided to use consumer hardware compared going with older data center servers. My first attempt was a dual E5-2620 v2 system (which is now a designated Proxmox home lab environment). The idea was to build a small footprint system that could live in a living room without being noticed. The choice came down to either an mATX or ITX-sized motherboard. I ended up going with ITX because my only need for connecting devices to the server would be hard drives. These would be connected via an HBA card due to the limited number of native SATA connectors on an ITX motherboard, as well as the mix of SAS and SATA drives I had.\nCPU \u0026amp; Memory Choice # When it came to picking the right CPU for the media server, I faced a dilemma: I had to choose between utilizing a fully fledged graphics card for transcoding media or going with an iGPU solution. After researching Intel Quick Sync and AMD options, I settled on a relatively modern Intel CPU capable of handling all the tasks, including transcoding multiple 4K streams if need be.\nI wanted to keep the system as small as possible and saw no real benefit to using a dedicated graphics card (even before the world went insane with GPU prices). With the CPU chosen, I decided on 32GB of RAM to ensure I would be set for the near future regardless of the workload I threw at the system. (Ah, the good ol\u0026rsquo; days, when RAM prices were sane.) Since this particular server wouldn\u0026rsquo;t have any critical workloads, I decided to skip ECC and go with a regular consumer DDR4 2x16GB kit.\nStorage \u0026amp; Case # For storage, I had a mix of new and used SAS and SATA drives that I had gathered over time. The current setup features drives ranging from 8TB to the unusual 14TB size. The drives are all connected to an IT-flashed HBA card via mini-SAS to SATA breakout cables.\nThe HBA card was not designed to be used in a normal PC case where static air pressure is relatively low. Thankfully, there are many 3D-printable files available that allow you to mount a small fan to the card to keep its temperatures in check.\nAt the time, there weren\u0026rsquo;t a lot of options for NAS cases that were small and could hold up to 6 drives. The case that met all my size and drive-count requirements was the Fractal Design Node 804. For the main operating system, the choice was obvious: a decently sized (1TB) NVMe drive. Luckily, the motherboard supported two NVMe drives (since I wasn\u0026rsquo;t using the onboard SATA ports), which will allow me to add another drive down the line to mirror them.\nThe Result # The result is a very dense, capable, and relatively quiet system (though the SAS drives are loud at times). It can be placed anywhere in the living room without being an eyesore, all while holding tens of terabytes of media files and transcoding them smoothly without completely breaking the bank.\n","date":"14 May 2026","externalUrl":null,"permalink":"/projects/home-media-server/hardware/","section":"Projects","summary":"","title":"Hardware","type":"projects"},{"content":"","date":"14 May 2026","externalUrl":null,"permalink":"/tags/hardware/","section":"Tags","summary":"","title":"Hardware","type":"tags"},{"content":" Operating System and Software Choice # To VM or Not to VM # Many people virtualize their media server operating system be it Linux windows, or even freeBSD. That was also my first choice when I first started (the system that is now a Proxmox home lab) it has its advantages like easy full system snapshots and roll backs in the event something goes wrong, as well as the ability to be high availability if I ended up deploying more proxmox servers.\nPersonally as I was planing on running all of my applications in containers anyway, I didn\u0026rsquo;t really find the need to have another layer of complexity on top of the base Linux install, a layer which I needed to manage upgrade as well as the added complexly of passing both the drives and the iGPU to the VM. Which is why I decided on going with a bare metal install. When considering the needs and wanting the setup to be as self sufficient and off hands after getting it configured it made the most sense.\nWhat Flavor to pick? # When it came to picking the flavor of Linux I had a few options, I knew for sure I wont be going or touching windows for this project, as it was just the wrong tool for the job.\nFirst possible candidate was OpenMediaVault (OVM) which is based on Debian and has a nice web UI to which allows some easy configuration and overview of the system. It had support for both mergerFS and SnapRAID and looked like a strong choice. After looking deeper into OVM and deploying it in my home lab to play around with it, I decided it was not the right choice.\nI wanted the setup to be as simple as possible having another layer on top of Debian where all the configuration was stored in a database didn\u0026rsquo;t not speak to me, the more I read and saw issues and errors which were due to the usage of the database component which stored all the configuration.\nThe Second Choice I was looking at the time was TrueNAS, which back then was still based on freeBSD and the Linux version was not out yet, unfortunately it required ZFS which I didn\u0026rsquo;t not want or could commit to due to my drive sizes and not wanting to commit to a data pool size upfront. It was before ZFS added the ability to adjust the pool size (recently). I also deployed it in the proxmox and didn\u0026rsquo;t like the extra layers it had on top.\nAll of the \u0026ldquo;User friendlily \u0026quot; options just didn\u0026rsquo;t speak to me, I wanted the setup to be as simple and stable as possible, which lead me to using plain good ol headless install of Debian.\nWhen it comes to Linux distributions for stability and dependency I don\u0026rsquo;t think there is beating Debian as the father and grandfather of many modern Linux distributions. It has always been my first choice when it comes to a minimal install of a Linux server.\nDebian does have its cons of due to being so stable and behind the cool new kids, it gets its toys always almost after everyone. Which didn\u0026rsquo;t present itself as an issue until I was configuring transcoding using the iGPU (well get there).\nI was familiar with Debian as well APT and it was a no brain choice for me to pick it as the base for the media server as its widely supported and used int the wild.\n","date":"14 May 2026","externalUrl":null,"permalink":"/projects/home-media-server/operating-system/","section":"Projects","summary":"","title":"Operating System","type":"projects"},{"content":"","date":"14 May 2026","externalUrl":null,"permalink":"/tags/server/","section":"Tags","summary":"","title":"Server","type":"tags"},{"content":" What is a media stack? # The goal is to offer a hands free media hub to allow myself, friends and family to access and view media in single place. The core service which would be user facing in this deployment would be:\nJellyfin - The main application serving the media Seerr - Allowing user to request media they would like the view Most of the service/applications in this stack could be used as standalone but they also are made to integrate which other. The \u0026ldquo;backend\u0026rdquo; consists of the following service:\nRadarr - Media management tool for movies Sonarr - Shows and TV management tool Bazzar - Subtitles management tool All of the services are going to be connected to a revers proxy:\nNginx or Swag - Swag is based on Nginx and comes with a few templates and plug-ins to connect to Cloudflare in order to help us with SSL certifications automation Media files I wont be covering acquiring media files in this short write down. Buying physical media and ripping the media to file yourself. As well as sailing the seas, I will leave that part to the reader to figure out. Now that we have an idea of what we want to deploy we need to create our file structure, users and decide on how we would like to deploy our applications.\nFile system configuration # Aside from our default file system configuration we also need to configure our MergerFS directory structure, as we are going to need to mount certain directories as volumes for our services. The most recommended way would be to have everything under a single directory.\n/ └── data/ ├── configs/ └── media/ ├── movies/ └── shows/ ","date":"14 May 2026","externalUrl":null,"permalink":"/projects/home-media-server/services/","section":"Projects","summary":"","title":"Services","type":"projects"},{"content":"","date":"14 May 2026","externalUrl":null,"permalink":"/tags/virtual-machines/","section":"Tags","summary":"","title":"Virtual Machines","type":"tags"},{"content":" Storage Storage Management \u0026amp; Pooling Hardware Hardware choice for my home server Operating System Operating system and software choice for the media server Services Home Media Server Service Choice Overview # The idea was to build a small footprint server which would follow the KISS (\u0026ldquo;Keep it Simple, Stupid\u0026rdquo;) principles and would just work and stay stable. Which is how I ended up with the following setup.\nA container centric deployment of bare metal Debian installation of which the primary goal was to automate media management and access. The server runs multiple services in docker containers which are configured via docker compose files. The media stack is made of \u0026ldquo;frontend\u0026rdquo; services like \u0026ldquo;Jellyfin\u0026rdquo; which is used to serve the media, while the \u0026ldquo;backend\u0026rdquo; uses arr services like (sonarr and radarr) to manage the media.\nThe \u0026ldquo;frontend\u0026rdquo; services are peroxided via Nginx, while the backend services are accessible via FQDN by using a split DNS, and restricting access to them from outside of the local network. The deployment is supplemented by a edge installation of Opensense which acts as the local DNS and DHCP server (installed on a physical server).\nThe media files are stored on a local array of multiple drives which are grouped together under Mergerfs in combination with SnapRAID in order to provide redundancy to the deployment.\nThe server is hosting several other services like a Nextcloud installation, but this short series would mainly focus on tools choice architecture and implications it would assume the reader is familiar with containers as well as has some understanding of networking and Linux.\n","date":"13 May 2026","externalUrl":null,"permalink":"/projects/home-media-server/","section":"Projects","summary":"","title":"Home Media Server","type":"projects"},{"content":"","date":"11 May 2026","externalUrl":null,"permalink":"/tags/data-models/","section":"Tags","summary":"","title":"Data Models","type":"tags"},{"content":" Data Models # Before going into any logic and what the bot itself is able to do, I would need to explain the building blocks and the foundations of bot, which are data classes. From the very begging of the first single file script I have decided I wanted to use a sqlite database, and not a CSV file, JSON or \u0026ldquo;sudo database\u0026rdquo;\nThe core design of the bot is made around a few key python classes, which are also what the database scheme was designed around. Most of the logic of the bot is in its custom light ORM layer which is made to map the sqlite data to the python object as all of the bot logic is written around this Python classes . The idea was to mimic how larger ORM\u0026rsquo;s like Sql alchemy and sqlmodel map attributes to database columns. The movies object is the most basic building block of the data structure of the bot.\nThe first basic building block of the data structure if the \u0026ldquo;Movie\u0026rdquo; class. It hold information about the movie itself. From the very begging I have decided to standardize the user input being an IMDb link, as its the most well known \u0026ldquo;Movie database\u0026rdquo;.\nYou might ask yourself where does the movie data come from? I have settled on the TMDB as the single source of truth for all the external data the bot requires. I will cover more about the TMDB in another section.\n# The movie object which hold all the data for a movie (some methods are not included in this snippit) @dataclass(kw_only=True) class Movie: imdb_id: str tmdb_id: int title: str release_date: date | None runtime_mins: int genres: str original_language: str plot: str budget: int revenue: int tmdb_vote_average: float tmdb_vote_count: int poster_link: str __db_table_name__ = \u0026#34;movie_data\u0026#34; @classmethod def build_from_row(cls, row: Row) -\u0026gt; Movie: return cls( imdb_id=row[\u0026#34;imdb_id\u0026#34;], tmdb_id=row[\u0026#34;tmdb_id\u0026#34;], title=row[\u0026#34;title\u0026#34;], release_date=date.fromisoformat(row[\u0026#34;release_date\u0026#34;]) if row[\u0026#34;release_date\u0026#34;] else None, runtime_mins=row[\u0026#34;runtime_mins\u0026#34;], original_language=row[\u0026#34;original_language\u0026#34;], genres=row[\u0026#34;genres\u0026#34;], plot=row[\u0026#34;plot\u0026#34;], budget=row[\u0026#34;budget\u0026#34;], revenue=row[\u0026#34;revenue\u0026#34;], tmdb_vote_average=row[\u0026#34;tmdb_vote_average\u0026#34;], tmdb_vote_count=row[\u0026#34;tmdb_vote_count\u0026#34;], poster_link=row[\u0026#34;poster_link\u0026#34;], ) The other basic building block of the data structure of the bot is the Submission class, which holds in itself the submitter_id which is the snowflake id discord provides each user. As well as the IMDb id of the movie the user is submitting, the current season, as well as the status of the movie (which defaults to the \u0026ldquo;nominated\u0026rdquo; status)\n# Submission class which hold infomraion about the submission user and movie id @dataclass(kw_only=True) class Submission: submitter_id: str imdb_id: str season_id: int status: str = \u0026#34;nominated\u0026#34; watched_date: Optional[date] = None __db_table_name__ = \u0026#34;movie_submissions\u0026#34; @classmethod def build_from_row(cls, row: Row) -\u0026gt; Submission: return Submission( submitter_id=row[\u0026#34;submitter_id\u0026#34;], imdb_id=row[\u0026#34;imdb_id\u0026#34;], season_id=row[\u0026#34;season_id\u0026#34;], status=row[\u0026#34;status\u0026#34;], watched_date=date.fromisoformat(row[\u0026#34;watched_date\u0026#34;]) if row[\u0026#34;watched_date\u0026#34;] else None, ) When combining both together they result in the core logic class which is used by almost every action in the bot has to preforms. They create the Movie Submissionclass, which is a child class of the Submission class, as it hold exactly the same information as the Submission class plus the nested Movie object that holds all the information about the movie which the submission is about.\n@dataclass(kw_only=True) class MovieSubmission(Submission): movie: Movie @classmethod def build_from_row(cls, row: Row) -\u0026gt; MovieSubmission: return MovieSubmission( submitter_id=row[\u0026#34;submitter_id\u0026#34;], imdb_id=row[\u0026#34;imdb_id\u0026#34;], season_id=row[\u0026#34;season_id\u0026#34;], status=row[\u0026#34;status\u0026#34;], watched_date=date.fromisoformat(row[\u0026#34;watched_date\u0026#34;]) if row[\u0026#34;watched_date\u0026#34;] else None, movie=Movie.build_from_row(row), ) ","date":"11 May 2026","externalUrl":null,"permalink":"/projects/movie-night-bot/data-models/","section":"Projects","summary":"","title":"Data Models \u0026 Core Design","type":"projects"},{"content":"","date":"11 May 2026","externalUrl":null,"permalink":"/tags/database-design/","section":"Tags","summary":"","title":"Database Design","type":"tags"},{"content":" Table of Content: # Data Models \u0026amp; Core Design A breakdown of the core code structure and the SQLite database. Overview # The goal of the project was to create a discord bot that would manage our friends groups \u0026ldquo;Movie Nights\u0026rdquo; the movie nights consist of every member submitting a movie for our groups to watch on our weekly movie nights.\nIn order to pick which movie is being viewed first we would random the movies using various tools, the methods were manual and cumbersome, which lead to the creation of this project.\nCurrent Features # Usage of modern discords slash commands Management and tracking of submitted movies per user/season Automatic watch-list tracking via TMDB custom lists (Global and per user) Automatic data storage/caching for submitted movies via a local SQLite database (TMDB as the data source) Fully asynchronous code base Usage of a lightweight custom ORM User rating of watched movies Usage of discord embeds for public messages Unified logging into daily files Below is the complete breakdown of how this was built from scratch. ","date":"11 May 2026","externalUrl":null,"permalink":"/projects/movie-night-bot/","section":"Projects","summary":"","title":"Movie Night Bot","type":"projects"},{"content":"","date":"11 May 2026","externalUrl":null,"permalink":"/series/movie-night-discord-bot/","section":"Series","summary":"","title":"Movie-Night Discord Bot","type":"series"},{"content":"","date":"11 May 2026","externalUrl":null,"permalink":"/tags/orm/","section":"Tags","summary":"","title":"ORM","type":"tags"},{"content":"","date":"11 May 2026","externalUrl":null,"permalink":"/tags/python/","section":"Tags","summary":"","title":"Python","type":"tags"},{"content":"","date":"11 May 2026","externalUrl":null,"permalink":"/tags/sqlite/","section":"Tags","summary":"","title":"SQLite","type":"tags"},{"content":"","externalUrl":null,"permalink":"/authors/","section":"Authors","summary":"","title":"Authors","type":"authors"},{"content":"","externalUrl":null,"permalink":"/categories/","section":"Categories","summary":"","title":"Categories","type":"categories"}]