Projects / Self-Hosted Infrastructure

Self-Hosted Infrastructure

A personally operated server stack for running services on hardware I control - with controlled ingress, clear boundaries, and a setup I can actually figure out when something breaks.

Proxmox Docker Linux Cloudflare Tunnel HAProxy CrowdSec Storage
Design constraint: Everything runs on hardware I control. If I can't deploy it, back it up, and restore it myself, it's not an acceptable dependency.
There is one exception: I need Cloudflare Tunnel for reasons described below, so that's the only externally controlled source. However, all content is E2EE, so all my data continues to be fully under my control.

At a glance

Platform

  • Host: Proxmox with a dedicated passthrough host OS
  • Primary VM: Ubuntu Server (aka, "the Sarlacc Pit") running Docker Compose
  • Perimary Workstation: Manjaro running on the same hardware as the rest of the site
  • Ingress: Cloudflare Tunnel -> HAProxy -> internal services
  • Public exposure: Website only

Core priorities

  • Single-entry point model: no direct port forwarding. Everything is forwarded through CF Tunnel
  • Side-by-side allocation: I want my main workstation (Manjaro) to be on the same hardware as the rest of the system. If my Manjaro is turned down, the Sarlacc Pit stays up. If the Sarlacc Pit goes down, my Manjaro system is not affected.
  • Service isolation: networks with limited definition, limited reachability
  • Control: easy to understand upgrades and configuration changes, so I have limited issues dealing with my system

Hardware

Computer

  • CPU: Ryzen 9 5950X (16C / 32T)
  • CPU split: cores are partitioned between virtualization workloads and a passthrough host OS
  • RAM: 16 GB DDR4
  • Motherboard: ASRock X570M Pro4
  • GPU: RTX 2060 (PCIe passthrough to a dedicated host OS)

Storage inventory

  • NVMe: 1 TB
  • SSDs: 640 GB, 480 GB, 256 GB
  • HDDs: 2 TB x 2, 12 TB x 1
  • Filesystems: LVM (boot NVMe), EXT4 (most disks), XFS (12 TB media)
Expanded image

System maps

I'm trying to show exactly how requests enter the system, where services live, and how storage is laid out.

Diagram 1: Architecture map
Bare metal -> Proxmox -> VMs/LXCs -> Docker stacks
TODO: /assets/img/diagrams/infra-architecture.svg
Top-level architecture: hypervisor, guests, and where services run.
Diagram 2: Network & ingress
Internet -> Cloudflare Tunnel -> HAProxy -> services
TODO: /assets/img/diagrams/infra-network.svg
Entry model: single entry point, intentional exposure, everything else private.
Diagram 3: Storage layout
Drives -> filesystems -> what lives where
TODO: /assets/img/diagrams/infra-storage.svg
Storage map: physical disks, filesystems, and dataset roles.
Diagram 4: Service segmentation
Core infra vs personal vs public
TODO: /assets/img/diagrams/infra-segmentation.svg
Service grouping and isolation: what can reach what, and why.

Virtualization layout

LXC 101 - Cloudflare ingress

  • OS: Debian-based
  • Purpose: Cloudflared (and lightweight terminal access)
  • Resources: 1 core, 256 MB RAM, ~2 GB disk
  • Why: tiny footprint, minimal surface area, dedicated role

VM - “Sarlacc Pit” (Ubuntu Server)

  • Purpose: main service host (Docker Compose)
  • Resources: 7 cores, ~7.3 GB RAM, ~220 GB system disk
  • Storage: attached bulk disks for app data + media

Services

Deployed with Docker Compose on the Ubuntu VM. The personal site is the only public-facing service.

Core infrastructure

  • HAProxy: reverse proxy / routing
  • Cloudflare Tunnel: ingress (no direct port forwarding)
  • CrowdSec: detection + blocking for abusive patterns
  • Gluetun: network isolation for selected traffic

Personal & internal services

  • Home Assistant
  • Jellyfin
  • Nextcloud
  • Jellyseerr
  • Sonarr / Radarr / Prowlarr
  • qBittorrent
  • Website

Security model

Ingress rules

  • Default: private
  • Public: website only
  • Admin access: gated behind Cloudflare Access + 2FA
  • No: direct port forwarding

Preventative controls

  • CrowdSec: watches for repeated failures and abusive clients
  • Segmentation: services are not implicitly reachable
  • Deliberate exposure: nothing is exposed "because it's easier"

What I chose not to do

  • No Kubernetes: at this scale, Docker Compose gives me the control I want without adding orchestration complexity.
  • Limited automation: I automate repeatable setup, but avoid “hands-off” systems that hide failure modes.
  • No managed services: I keep data and dependencies local so restore and recovery don't depend on external platforms.

Next steps

  • Stricter role separation for sensitive services.
  • More aggressive backup verification.
  • Documented recovery procedures for the critical pieces.