AW

Adam Worden

Inside My Home Lab - Hardware, Self-Hosted Services, and Architecture

Inside My Home Lab - Hardware, Self-Hosted Services, and Architecture

3 May 2026 · 11 min read

homelabself-hostingdockerbackupstunneling

Introduction

I started my Home Lab to learn how server deployment actually worked, and to host a few custom Discord bots. It turned into something a lot bigger than that. The first setup was modest: a single Raspberry Pi running six to eight PM2 processes, and that tiny board handled everything I threw at it for over a year.

Home Labs rarely stay small, however. I kept adding layers, a NordVPN Meshnet node here, a Plex media stack there, and the limits of single-board computing started to show. The setup grew sluggish and services would drop out at random, which made it clear that if I wanted something resilient I was going to need real hardware.

Hardware evolution

[ Raspberry Pi 3B ]  --->  [ Raspberry Pi 5 (8GB) ]  --->  [ Beelink EQ14 ]
  (Foundational Node)          (Databases & APIs)          (Primary Compute)

Raspberry Pi 3B

The Raspberry Pi 3B is where I started, back in 2023. It was tougher than I expected, hosting my early PM2 bots and a few simple websites for friends. It's retired from the stack now, but it's the board I learnt everything on, and it had a second act for a while as a dedicated VPN server for remote access.

Raspberry Pi 5

The Raspberry Pi 5 (8GB) joined in December 2024, running Ubuntu Server, to take the heavier workloads off the 3B. By mid-2025 I'd moved my main development stack over to TypeScript and shifted applications into Docker containers, and the Pi 5 kept my local and production environments in sync. This is important because it allowed me to develop against the same runtime I was shipping to, rather than catching the differences after deploy. It now hosts my core Database instances, my custom Roblox API gateways, and a self-hosted Kanban tool, Plankaban.

Beelink EQ14

In February 2026 I brought in the Beelink EQ14 as the primary compute engine. It runs an Intel N150 (4 cores, 4 threads) with 16GB of DDR4 and a 1TB NVMe SSD, which cleared up the compute and I/O bottlenecks I'd been fighting. I wiped the pre-installed Operating System and put Ubuntu Server on it, to keep everything lean for headless work.

The NVMe holds the OS, container images, and application data. A separate 1TB HDD hangs off the EQ14 for the bulk stuff, the Jellyfin media library and the Paperless-ngx document archive, which keeps the big files off the faster drive and leaves me room to grow.

That bulk data currently sits on a single drive with no local redundancy. This is a deliberate trade-off, due to the fact that the records in Paperless-ngx that actually matter are covered by off-site S3 backups. The plan is to move file storage onto a dedicated, rack-mounted NAS running RAID 10, so that I've got proper local redundancy and I'm not leaning entirely on off-site recovery.

The upgrade got me two things straight away:

  • Intel QuickSync: Transcoding high-bitrate media in Jellyfin stopped buffering, and it did it without hammering the CPU.
  • Faster builds: Docker build times went from 15 minutes to a couple.

Future networking plan: The EQ14 has dual Ethernet ports, and my next network iteration is to use them to segment the LAN, putting homelab services on their own VLAN next to a standalone physical security network.

Self-hosted services

With the EQ14 handling primary compute, I split the service architecture into layers: productivity, media, custom development, and system management.

+-----------------------------------------------------------------------+
|                         BEELINK EQ14 (Primary)                        |
|  +--------------------+  +--------------------+  +-----------------+  |
|  |   Document & Wiki  |  |    Media Stack     |  |  Custom Tools   |  |
|  | • Paperless-ngx    |  | • Jellyfin         |  | • Rehook        |  |
|  | • Outline          |  | • Jellyseerr       |  | • Discord Bots  |  |
|  |                    |  | • Servarr Suite    |  |                 |  |
|  +--------------------+  +--------------------+  +-----------------+  |
+-----------------------------------------------------------------------+
                                   |
                                   v
+-----------------------------------------------------------------------+
|                       RASPBERRY PI 5 (Secondary)                      |
|  +--------------------+  +--------------------+  +-----------------+  |
|  |  Project Tracking  |  | Integrations & DBs |  |   Management    |  |
|  | • Plankaban        |  | • Roblox API GW    |  | • Portainer     |  |
|  |                    |  | • PostgreSQL/Redis |  | • Cockpit       |  |
|  +--------------------+  +--------------------+  +-----------------+  |
+-----------------------------------------------------------------------+

Document management and knowledge base

  • Paperless-ngx: My document digitisation pipeline, running on the EQ14 with dedicated postgres, redis, gotenberg, and tika containers behind it. It OCRs anything I upload, screenshots or PDFs, so that I can search the text later and generate temporary share links.

  • Outline: My knowledge base and runbook store, and where I dump ideas. It has its own isolated PostgreSQL and Redis pair, to ensure that its data stays separate from everything else running on the node.

  • Plankaban: A private Kanban board running in Docker on the Pi 5. It's the roadmap hub for my Roblox game projects and infrastructure milestones.

Media server

  • Jellyfin & Jellyseerr: Jellyfin is my media server for legally-obtained movies and shows, to watch and share with family. Jellyseerr handles requests, and encoding is offloaded to the EQ14's QuickSync.

I ran Plex originally, but a few things pushed me off it. Features I wanted kept ending up behind a Plex Pass subscription, metadata and account auth ran through Plex's cloud, and it never stopped nudging me towards its own discovery and streaming products. Jellyfin is open-source, doesn't paywall hardware transcoding, and phones home to nobody. Once QuickSync was doing the heavy lifting, playback was as good as Plex had ever been, and the whole thing was mine.

Integrations and external services

  • Roblox API Gateway: A private gateway on the Pi 5 that bridges Roblox game servers to external data. It's what allows in-game features like custom player-voting to talk directly to outside databases and apps.

  • Rehook: Something I built to catch webhook payloads from services that only emit raw data, Outline Wiki for one, then process and tidy them up.

  • Discord Bots: Containerised TypeScript bots on the EQ14, Docket Bot and Commerce Service Desk among them, plus the legacy PM2 bots I'm phasing out on the Pi 5.

Container and system administration

  • Portainer: Running on both boxes, Server and Agent across the EQ14 and Pi 5, as one visual console for container lifecycles and environment variables.

  • Cockpit: Also on both nodes, giving me a web terminal and host management, disk usage, updates, and live metrics, straight from a browser.

Keeping services updated

A dozen-plus containers across two nodes means image updates stack up fast. I still do all of it by hand. That's on purpose, but it's also the part of the stack most in need of automation.

  • Manual rollouts: To update a service I read the release notes for anything breaking, pull the new image, and redeploy through Portainer or recompose on the host. Anything with its own Database I treat carefully, and never before I've confirmed a fresh backup.
  • Host patching: OS and security updates on both Ubuntu Server nodes go on by hand too, during planned maintenance windows, alongside the container updates.
  • Where this is headed: Tracking releases manually across this many services won't scale, so automating it is a near-term priority. First step is release notifications, probably Watchtower in monitor-only mode, so that I at least know when new images land. After that, reviewed automated rollouts as part of the Infrastructure as Code work on my roadmap, with unattended-upgrades taking care of host security patches. The point is to make rollouts repeatable and version-controlled instead of a manual chore.

Architecture and security

A reliable home lab takes more than spinning up services. Access has to be locked down, health has to be visible, and the data that matters has to survive a dead drive.

+-------------------------------------------------------------------------+
|                              PUBLIC INTERNET                            |
+-------------------------------------------------------------------------+
       |                                      |
       | (Zero-Trust Ingress)                 | (Off-Grid Status Page)
       v                                      v
+-----------------------+              +----------------------------------+
|  Cloudflare Tunnels   |              |   UptimeFlare (Cloudflare Pages) |
+-----------------------+              +----------------------------------+
       |                                      |
       +--------------+                       | (Monitors Health)
                      |                       v
                      v                +----------------------------------+
          +-----------------------+    |        Local Home Lab            |
          |  Local Web Services   |    |  (EQ14 & Raspberry Pi Stack)     |
          +-----------------------+    +----------------------------------+
                      |
                      | (Nightly Incremental Sync)
                      v
          +---------------------------------------------------------------+
          |                   AWS S3 BACKUP PIPELINE                      |
          |  Standard (0-30 days) -> S3-IA (30-90 days) -> Glacier (90+)  |
          +---------------------------------------------------------------+

Ingress and remote access

Rather than open router ports or wrangle port-forwarding tables, and put my public IP in front of every scanner on the internet, I route external traffic through Cloudflare Tunnels. The tunnel is an encrypted, outbound-only connection from my network to Cloudflare's edge, which is to ensure that I can reach Outline or Paperless-ngx from anywhere, whilst getting Cloudflare's DDoS protection for free and never exposing a port to the public internet.

cloudflared runs in Docker with one tunnel per application. Each tunnel points straight at its service over container:port on the internal Docker network, so that requests land on the right container and nothing is exposed on the host.

Health monitoring

To watch availability without depending on the very hardware I'm watching, I run UptimeFlare on Cloudflare Pages.

  • Off-grid reliability: It lives entirely on Cloudflare's serverless network, so that the status page stays up even if my home network or power doesn't.
  • Custom branding: I reskinned the default template to match my own domain, so that uptime and latency across every node sit on one dashboard.

Secrets management

A dozen-plus containers means a lot of sensitive config: API keys, database passwords, the .env files holding it all together. All of it lives in a KeePass Database, my single source of truth for credentials across my files and devices.

Right now that vault sits in a cloud store so that it stays synced across my devices. Convenient, yes, but trusting a third party with my most sensitive data is exactly the dependency this whole project exists to shed, so self-hosting the vault sync is on the roadmap.

Nightly backups (AWS S3)

Not losing data matters, particularly the digitised personal records in Paperless-ngx. To keep backups safe without paying for hot storage I never touch, I set up an AWS S3 lifecycle pipeline:

  1. Incremental sync: Daily Paperless-ngx backups and database snapshots sync to a versioned S3 bucket, encrypted at rest with AWS KMS-managed keys (SSE-KMS).
  2. S3 Standard (Days 1-30): Recent backups, available for immediate retrieval.
  3. S3 Infrequent Access (Days 30-90): Older data moves to S3-IA, cheaper to store but still on hand if I need it.
  4. AWS Glacier (Day 90+): Anything past 90 days drops to Glacier Deep Archive for long-term cold storage at rock-bottom cost.

Bottlenecks and lessons learned

Building this stack out has been one of my favourite projects, and moving from one Raspberry Pi to an x86 box changed how I run everything. It also taught me a few things the hard way.

  • Hardware acceleration matters more than I thought: Software encoding falls apart the moment a few people start streaming at once. Moving to an Intel chip with QuickSync was the single biggest jump in day-to-day experience.
  • Architecture changes the rules: ARM boards sip power, which is great, but a large Docker build on one can take 15 minutes or more. Shifting the heavy build pipelines and main containers to x86 brought that back down to a couple of minutes.
  • Containers ended the "works on my machine" problem: Standardising on Docker across both nodes, and packaging each app with its database dependencies like Postgres and Redis, means local TypeScript development behaves exactly like production.

What's Next?

  1. Finish the Docker migration: I've started pulling the legacy PM2 apps off the Pi 5 and moving them into containers, to get to full containerisation on both nodes.

  2. Network-wide DNS with Pi-hole: I'm tempted to bring the retired 3B back as a Pi-hole box, for network-wide ad blocking and local DNS. A fitting job for the board that kicked all this off.

  3. Network segmentation via dual NICs: Using the EQ14's two Ethernet ports is high on the list. I want core homelab apps on one subnet and physical security/IoT gear walled off on a separate, firewalled VLAN.

  4. Automating service updates: Every container and host update is manual right now, which doesn't hold up across two nodes and a growing pile of services. Release notifications first, then reviewed automated rollouts, so that staying current stops eating my time.

  5. More Infrastructure as Code: Pushing further on repeatable container rollouts and automated updates across both nodes, so that config changes stay documented and reproducible.

  6. Dedicated NAS with RAID 10: Getting bulk storage off the EQ14's single HDD and onto a proper NAS in RAID 10, so that a dead drive isn't a call to my off-site S3 backups. It'll be rack-mounted as part of the rack build below.

  7. Self-hosted secrets sync: Bringing the KeePass vault sync in-house, so that my most sensitive data stops depending on someone else's cloud. One of the last external dependencies left to cut.

  8. An observability stack: Standing up Grafana and Prometheus to get past simple up/down checks. Real metrics would tell me how the network actually behaves, and the retired 3B could host it.

  9. A proper rack and managed networking: Longer term, I want everything in a dedicated rack, probably 8U or 12U: the compute nodes, the new NAS, and managed gear like a UniFi gateway and switches. Partly for the tidier footprint, mostly for the excuse to go deeper on networking with hardware that can actually do it.

Resources

Cloudflare Tunnels

Zero-trust ingress docs for exposing local services without opening ports.

developers.cloudflare.com

UptimeFlare

Open-source, Cloudflare Pages-based status monitor used for off-grid uptime tracking.

github.com

Paperless-ngx

Documentation for the document digitisation and OCR pipeline.

docs.paperless-ngx.com

Outline

Self-hosted knowledge base and wiki used for runbooks and notes.

docs.getoutline.com

Jellyfin

Free, self-hosted media server docs, including hardware acceleration setup.

jellyfin.org

Cockpit

Browser-based Linux server management interface.

cockpit-project.org

Written By

AW

Adam Worden

I'm a consultant, engineer, and life-long learner. I explore the intersection of cloud, security, and human-centric tech through a personal, garden-like approach to documentation.

Actions

Share PostPrint Post

Details

11 min read

Published on Sunday, 3 May 2026

Last Updated on Monday, 10 August 2026