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.
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.
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)
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
Bare metal -> Proxmox -> VMs/LXCs -> Docker stacks
TODO: /assets/img/diagrams/infra-architecture.svg
Diagram 2: Network & ingress
Internet -> Cloudflare Tunnel -> HAProxy -> services
Internet -> Cloudflare Tunnel -> HAProxy -> services
TODO: /assets/img/diagrams/infra-network.svg
Diagram 3: Storage layout
Drives -> filesystems -> what lives where
Drives -> filesystems -> what lives where
TODO: /assets/img/diagrams/infra-storage.svg
Diagram 4: Service segmentation
Core infra vs personal vs public
Core infra vs personal vs public
TODO: /assets/img/diagrams/infra-segmentation.svg
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.