- PHP 47.4%
- TypeScript 36.4%
- Python 5.2%
- HTML 4.6%
- Hack 2.9%
- Other 3.5%
| .github/workflows | ||
| docs | ||
| infrastructure | ||
| scripts | ||
| services | ||
| .env.example | ||
| .gitignore | ||
| .gitleaks.toml | ||
| INDEX.md | ||
| README.md | ||
Homelab Infrastructure
A reference implementation of a self-hosted home infrastructure stack: 60+ self-hosted services across 100+ containers, 33 TB storage, zero port-forwarding. Runs on NixOS with DPlaneOS, a self-developed NAS management layer. Currently migrating storage from BTRFS RAID5 to ZFS RAID-Z2.
INDEX.md - complete map of every file in this repository.
Contents
- Architecture
- Security model
- Service matrix
- Storage strategy
- Repository structure
- Tech stack
- Cost analysis
- Documentation
- Quick start
Architecture
Internet
│
▼
VPS - IONOS, Berlin (2 vCPU / 2 GB RAM / 80 GB NVMe)
├─ UFW firewall ports 80, 443, 51820 only
├─ CrowdSec IDS/IPS community blocklists, Traefik bouncer
├─ Traefik v3 TLS 1.3 termination, Let's Encrypt
└─ Gerbil WireGuard endpoint (Pangolin component)
│
│ WireGuard tunnel - ChaCha20-Poly1305, encrypted
▼
Raspberry Pi 5 - local LAN hub (Pangolin brain)
├─ Pangolin server identity, routing control plane
├─ Newt tunnel termination
└─ Traefik (inner) internal routing → services
│
▼
Home network - 10.XXX.0.0/24
├─ NAS server (10.XXX.0.1) 100+ containers, 33 TB storage
└─ Pi-hole (10.XXX.0.2) DNS, ad-blocking
Key design decision: Pangolin tunnel instead of port-forwarding means the home IP is never exposed, there is no dynamic DNS dependency, and the home router attack surface is zero.
Interactive diagrams: Architecture · Security layers · Data flow
Security model
Defense in depth: each layer is independently hardened and does not rely on the correctness of outer layers.
Layer 1 - Perimeter (VPS)
UFW: only ports 80, 443, 51820 reachable from internet
CrowdSec: IP reputation, automated banning, community threat feeds
DDoS mitigation: IONOS network-level
Layer 2 - Transport
WireGuard: encrypted tunnel between VPS and home network
TLS 1.3: enforced at both outer (VPS Traefik) and inner (Pi Traefik) layers
Let's Encrypt: certificates auto-renewed, HSTS enabled
Layer 3 - Network isolation
Docker bridge networks scoped per service group
Databases not exposed to host network interface
Services communicate only via explicitly defined networks
Layer 4 - Application
Per-service authentication; OIDC where supported
2FA enforced on Vaultwarden and Nextcloud
Vaultwarden for shared credential management
Layer 5 - Data integrity
ZFS end-to-end checksumming: every block verified on read, silent corruption repaired from RAID-Z2 parity
RAID-Z2: dual-disk fault tolerance, no write-hole vulnerability (unlike BTRFS RAID5)
Scheduled scrubs verify the full pool
Current: migrating from BTRFS RAID5 - see infrastructure/storage/README.md
Service matrix
Media
| Service | Purpose | Compose |
|---|---|---|
| Emby | Media server | arr-suite.yml |
| Sonarr | TV show automation | arr-suite.yml |
| Radarr | Movie automation | arr-suite.yml |
| Lidarr | Music automation | arr-suite.yml |
| Prowlarr | Indexer management | arr-suite.yml |
| Bazarr | Subtitle automation | arr-suite.yml |
| FlareSolverr | Cloudflare bypass for indexers | arr-suite.yml |
| Recyclarr | TRaSH Guides quality profile sync | arr-suite.yml |
| Maintainerr | Media library cleanup rules | arr-suite.yml |
| Seerr | Media request management | arr-suite.yml |
| qBittorrent | Download client (via Gluetun VPN) | arr-suite.yml |
| Gluetun | WireGuard VPN gateway for downloads | arr-suite.yml |
| Audiobookshelf | Audiobooks + podcast server | books.yml |
| Grimmory | Ebook library (Booklore-based, MariaDB) | books.yml |
| Bookkeep | Book discovery and request manager | books.yml |
| Readmeabook | Audiobook request manager | books.yml |
| SwingMusic | Self-hosted music player | swingmusic.yml |
| Navidrome | Music streaming (Subsonic API) | navidrome.yml |
| Pinchflat | YouTube archiver | pinchflat.yml |
| Stremio Server | Streaming add-on server | stremio.yml |
Productivity & cloud
| Service | Purpose | Compose |
|---|---|---|
| Nextcloud + Collabora | File sync, cloud office suite | nextcloud.yml |
| Nextcloud Talk HPB | High-performance video call backend | nextcloud.yml |
| Immich | Google Photos replacement | immich.yml |
| CryptPad | End-to-end encrypted collaborative office | cryptpad.yml |
| TriliumNext Notes | Hierarchical personal knowledge base | trilium.yml |
| Glass Keep | Lightweight notes app | glass-keep.yml |
| Librum | Self-hosted ebook reading and library | librum.yml |
| BentoPDF | PDF tools web app | bentopdf.yml |
| Revolt.chat | Self-hosted Discord alternative | revolt.yml |
| Paperless-NGX | Document OCR and archive | paperless.yml |
| Vaultwarden | Self-hosted Bitwarden server | vaultwarden.yml |
| Joplin Server | Note sync backend | joplin.yml |
| Memos | Lightweight notes / journal | memos.yml |
| Linkwarden | Bookmark archiver with full-page capture | linkwarden.yml |
| Wallos | Subscription and expense tracker | wallos.yml |
Infrastructure & management
| Service | Purpose | Compose |
|---|---|---|
| Pi-hole | Network-wide DNS + ad-blocking | pihole.yml |
| Newt | Pangolin tunnel client | newt.yml |
| Tailscale DERP | Self-hosted Tailscale relay server | tailscale-derper.yml |
| Traefik v3 | Reverse proxy, TLS termination | VPS-managed - see infrastructure/networking/traefik/ |
| Pangolin + Gerbil | Self-hosted Cloudflare Tunnel | VPS + Pi - see infrastructure/networking/pangolin/ |
| CrowdSec | IDS/IPS, collaborative threat intel | VPS-managed - see infrastructure/security/crowdsec/ |
| MinIO | S3-compatible local object storage | minio.yml |
| Beszel | Lightweight server resource monitoring hub | beszel.yml |
| Beszel Agent | Per-host metrics collector | beszel-agent.yml |
| Dockge | Docker Compose management UI | dockge.yml |
| Dockpeek | Container health dashboard | dockpeek.yml |
| Scrutiny | S.M.A.R.T disk health monitoring | scrutiny.yml |
| Glances | System resource monitoring | glances-dashboard.yml |
| Homarr | Service dashboard | homarr.yml |
| Kasm | Containerized browser/desktop streaming | kasm.yml |
| Heaper | Docker host resource and container manager | heaper.yml |
| Arcane | Docker management UI | arcane.yml |
| Syncthing | Peer-to-peer file sync (config backup) | syncthing.yml |
| SearXNG | Self-hosted metasearch engine | searxng.yml |
| Zelest Stacks | Compose stack management UI | stacks.yml |
| PostgreSQL (×8) | Relational database instances | Per-service |
| Redis (×3) | In-memory cache instances | Per-service |
Development
| Service | Purpose | Location |
|---|---|---|
| DPlaneOS website | Project landing page (nginx static) | d-planeos-website.yml |
| Dan Portfolio | Personal portfolio site (PHP + Postfix relay) | dan-portfolio.yml |
| Forgejo | Self-hosted Git forge | forgejo.yml |
| Nginx CV | Personal CV/resume web page | nginx-cv.yml |
| Resume Builder | Next.js frontend + FastAPI backend + AI-powered PDF resume parser | resume-builder.yml |
Storage strategy
Full details: infrastructure/storage/README.md
33 TB across four HDDs - currently BTRFS on mdadm RAID5 (legacy, from ZimaOS). Migrating to ZFS RAID-Z2 via DPlaneOS.
Why ZFS
ZFS RAID-Z2 (dual-parity) eliminates the write-hole vulnerability present in BTRFS RAID5, provides end-to-end checksumming at every level of the storage tree, and integrates cleanly with DPlaneOS's pool and dataset management. zfs send replication makes off-site backup straightforward. The 15+ year production track record in enterprise environments makes it the right choice for a long-lived NAS.
Target pool layout
zpool: mainpool (RAID-Z2, 4× HDD)
├── mainpool/appdata /DATA/AppData - container bind-mount volumes
├── mainpool/media /DATA/Media - media library
├── mainpool/home user home directories
└── mainpool/backups local snapshot targets
zpool: bootpool (mirror, NVMe)
└── NixOS system root
Pool and dataset configuration is managed through DPlaneOS. Refer to that repository for NixOS module definitions and dataset property details (compression=zstd, xattr=sa, recordsize tuning per workload).
Repository structure
homelab/
├── infrastructure/
│ ├── networking/
│ │ ├── pihole/pihole.yml
│ │ ├── newt/newt.yml
│ │ ├── tailscale-derper/tailscale-derper.yml
│ │ ├── traefik/ # Traefik config notes (VPS-managed)
│ │ └── pangolin/
│ │ ├── README.md # Tunnel docs + component map
│ │ └── terraform/ # Infrastructure as Code
│ │ ├── main.tf / cloudflare.tf / vps.tf / backend.tf / variables.tf
│ │ ├── docker-compose.yml # Pangolin stack (deployed to VPS)
│ │ ├── config/ # Pangolin + Traefik configs
│ │ ├── scripts/backup.sh # Weekly backup → MinIO
│ │ ├── scripts/restore.sh # DR restore from MinIO
│ │ ├── minio/docker-compose.yml # MinIO on NAS (Dockge)
│ │ ├── pangolin-terraform-iac.md # IaC documentation
│ │ └── DR_RUNBOOK.md # Disaster recovery procedure
│ ├── security/
│ │ ├── vaultwarden/vaultwarden.yml
│ │ └── crowdsec/ # CrowdSec notes (VPS-managed)
│ ├── monitoring/
│ │ ├── beszel/beszel.yml
│ │ ├── beszel-agent/beszel-agent.yml
│ │ ├── big-bear-scrutiny/scrutiny.yml
│ │ ├── big-bear-dockge/dockge.yml
│ │ ├── big-bear-dockpeek/dockpeek.yml
│ │ └── glances-dashboard.yml
│ └── storage/
│ ├── minio/minio.yml
│ └── README.md
├── services/
│ ├── media/
│ │ ├── arr-suite.yml # Emby, Sonarr, Radarr, Lidarr, Prowlarr, Bazarr, qBittorrent, Gluetun, Seerr, Maintainerr
│ │ ├── books/books.yml # Audiobookshelf, Grimmory, Bookkeep, Readmeabook
│ │ ├── immich/immich.yml
│ │ ├── navidrome/navidrome.yml
│ │ ├── pinchflat/pinchflat.yml
│ │ ├── stremio/stremio.yml
│ │ └── swingmusic/swingmusic.yml
│ ├── productivity/
│ │ ├── nextcloud.yml # Nextcloud, Collabora, Talk HPB, Redis, PostgreSQL
│ │ ├── bentopdf/bentopdf.yml
│ │ ├── cryptpad/cryptpad.yml
│ │ ├── glass-keep/glass-keep.yml
│ │ ├── librum/librum.yml
│ │ ├── revolt/revolt.yml
│ │ ├── trilium/trilium.yml
│ │ ├── big-bear-joplin/joplin.yml
│ │ ├── big-bear-linkwarden/linkwarden.yml
│ │ ├── big-bear-paperless-ngx/paperless.yml
│ │ ├── big-bear-wallos/wallos.yml
│ │ └── memos/memos.yml
│ ├── management/
│ │ ├── homarr/homarr.yml
│ │ ├── kasm/kasm.yml
│ │ ├── heaper/heaper.yml
│ │ ├── arcane/arcane.yml
│ │ ├── stacks.yml
│ │ ├── syncthing/syncthing.yml
│ │ └── searxng/searxng.yml
│ └── development/
│ ├── d-planeos-website.yml
│ ├── dan-portfolio/dan-portfolio.yml
│ ├── forgejo/forgejo.yml
│ ├── nginx-cv/nginx-cv.yml
│ └── resume-builder.yml
├── scripts/
│ └── export-all-compose.sh
├── docs/
│ ├── hardware-specs.md
│ ├── homelab-complete-journey.md
│ ├── docker-infrastructure.md
│ ├── network-security.md
│ ├── network-remote-access.md
│ ├── media-stack.md
│ ├── productivity-services.md
│ ├── nextcloud-optimization-guide.md
│ ├── nextcloud-update-guide.md
│ ├── homelab-dashboard-guide.md
│ ├── pangolin-infrastructure.md
│ ├── pangolin-deployment-guide.md
│ ├── pangolin-configurations.md
│ ├── pangolin-vps-relay-guide.md
│ ├── pangolin-upgrade-guide.md
│ ├── pangolin-z-performance-tuning.md
│ ├── pangolin-traefikdashboard-guide.md
│ ├── NIXOS-MIGRATION.md
│ ├── DOCKER-SERVICES.md
│ ├── INDEX.md
│ └── *.html # Interactive architecture diagrams
├── docker/ # Legacy source tree - see docker/README.md
├── .env.example # All required environment variables
└── README.md
Tech stack
| Layer | Technology |
|---|---|
| Operating system | NixOS - declarative, reproducible, atomic OTA |
| NAS management | DPlaneOS (self-developed) - ZFS pool management, SMB/NFS, Docker orchestration, web UI |
| Container orchestration | Docker Compose |
| Reverse proxy | Traefik v3 - TLS 1.3, automatic Let's Encrypt |
| Tunnel | Pangolin - self-hosted Cloudflare Tunnel |
| Security | CrowdSec IDS/IPS |
| DNS | Pi-hole |
| Storage | OpenZFS RAID-Z2 (target) - migrating from BTRFS on mdadm RAID5 - 33 TB |
| Databases | PostgreSQL 14 (×8 instances), Redis Alpine (×3 instances) |
| Hardware | Intel i3-13100 / 32 GB DDR4-3200 / 120 GB NVMe |
| VPS gateway | IONOS Berlin - 2 vCPU / 2 GB / 80 GB NVMe |
Cost analysis
Initial investment
Hardware €1,290
VPS (3 yr) €180
Domain (3 yr) €36
────────────────────────────
Total €1,506
Annual operating cost
Electricity €75 (~30 W average, €0.30/kWh)
VPS €60
Domain €12
────────────────────────────
Total €147/yr (€12/month)
Replaced SaaS (annual)
Google One €30
Dropbox Plus €120
1Password €60
Spotify €180
Netflix €156
iCloud+ €36
────────────────────────────
Total replaced €582/yr
Net savings: €435/yr → break-even Year 4
Documentation
| Document | Description |
|---|---|
| Why DPlaneOS | ZimaOS limitations, alternatives considered, what DPlaneOS solves |
| Complete Journey | Full build narrative, decisions, and lessons learned |
| Hardware Specs | Component list, benchmarks, power draw |
| Docker Infrastructure | Container architecture and patterns |
| Network Security | Security layer detail |
| Remote Access | VPN comparison, Pangolin vs Tailscale vs ZeroTier |
| Pangolin Infrastructure | Tunnel architecture deep-dive |
| Pangolin Deployment Guide | Step-by-step VPS setup |
| Pangolin Configurations | Config file reference |
| Pangolin VPS Relay | Relay server setup |
| Pangolin Upgrade Guide | Version upgrade procedures |
| Pangolin Performance Tuning | Kernel sysctl optimisations |
| Pangolin Traefik Dashboard | Dashboard configuration |
| Pangolin HA Setup | Active-passive failover: Litestream B2 replication, Cloudflare DNS flip, ~7-8 min RTO |
| Media Stack | Arr suite + Emby configuration |
| Productivity Services | Nextcloud, Immich, Paperless setup |
| Nextcloud Optimisation | PostgreSQL + Redis + PHP tuning |
| Nextcloud Update Guide | Post-update occ commands, failure diagnosis, 502 behind Traefik |
| Dashboard Guide | Homelab dashboard setup |
| NixOS Migration Plan | ZimaOS → NixOS migration roadmap |
| Service Index | Full service inventory |
Quick start
# Clone
git clone https://github.com/4nonX/homelab
# Set up environment variables
cp .env.example .env
$EDITOR .env # fill in domains, passwords, IPs
# Deploy a stack - example: Nextcloud
cd services/productivity
docker compose -f nextcloud.yml up -d
# Deploy monitoring
cd infrastructure/monitoring
docker compose -f glances-dashboard.yml up -d
For the full external access setup (Pangolin tunnel + Traefik + CrowdSec on VPS), start with docs/pangolin-deployment-guide.md.
Built on the shoulders of giants
This homelab would not exist without these projects. Each one is worth knowing about.
🥇 Pangolin - the backbone of external access
The single most impactful piece of infrastructure in this stack. Pangolin is a self-hosted tunnelled reverse proxy with identity and access management - think Cloudflare Tunnel, but open source and entirely under your control.
Before Pangolin, the realistic options for external access were: open ports on your home router (bad), pay for a commercial tunnel (fine but a dependency), or set up WireGuard manually and manage it yourself (works, but significant operational overhead). Pangolin wraps WireGuard, Traefik, and an identity layer into something you can actually run on a cheap VPS and forget about. Zero port-forwarding. Zero home IP exposure. Let's Encrypt certificates handled automatically. The security architecture of this entire homelab is built around what Pangolin makes possible.
Components: Pangolin (control plane) · Gerbil (WireGuard gateway) · Newt (tunnel client) · Badger (identity)
If you self-host anything that needs external access, start here.
🗄️ DPlaneOS - the NAS management layer (self-developed)
A NAS management layer I built myself - ZFS pool management, SMB/NFS shares, Docker orchestration, and a unified web UI, purpose-built on top of NixOS. Developed as a high-performance Go daemon to fill the gap between CasaOS (simple but limited) and TrueNAS (powerful but container-hostile).
🐳 Core infrastructure
| Project | Role in this stack |
|---|---|
| The OS everything runs on. Declarative system configuration, atomic rollbacks, reproducible builds - the base that makes the whole stack manageable. DPlaneOS runs on top of it. | |
| Reverse proxy - TLS termination, automatic Let's Encrypt, routing. Runs on the VPS and handles every inbound HTTPS request. | |
| Collaborative IDS/IPS. Analyses Traefik logs, bans malicious IPs via bouncer, pulls community threat feeds. The passive security layer that works without any configuration after setup. | |
| Network-wide DNS and ad-blocking. Every device on the LAN benefits without any per-device configuration. | |
The filesystem everything is migrating to. End-to-end checksumming, RAID-Z2, snapshots, zfs send replication - the right foundation for a long-lived NAS. |
📦 Services
| Project | What it replaces |
|---|---|
| Google Drive, Google Docs, iCloud | |
| Google Photos | |
| 1Password, LastPass | |
| Manual document filing | |
| Netflix, Plex (self-hosted media server) | |
| Manual media management | |
| Per-client indexer configuration | |
| Manual subtitle hunting | |
| Any VPN client - containerised, provider-agnostic WireGuard gateway | |
| Dropbox, rsync scripts | |
Manual docker compose commands |
|
| Ignoring disk health until something fails | |
| Google, Bing | |
| Audible, podcast apps | |
| Spotify (self-hosted music streaming) | |
| Local music player apps | |
| YouTube Premium downloads | |
| Browser bookmarks, Pocket | |
| Notion, Apple Notes | |
| Manual subscription tracking | |
| Scattered system monitoring tools | |
| Evernote, Apple Notes (sync backend) |