Skip to content

Decision Log

Osotechie edited this page Jun 22, 2026 · 2 revisions

Decision Log

Key architectural and technology decisions made throughout this project, and the reasoning behind them. These are the "why did you choose X over Y?" answers.


Why MergerFS + SnapRAID over ZFS?

Decision: Use MergerFS to pool individual disks into a single namespace, with SnapRAID providing file-level parity protection.

Reasoning:

  • Flexibility — disks can be different sizes and added/removed independently
  • Simplicity — MergerFS is a FUSE filesystem, no special kernel modules or pool management
  • No vendor lock-in — each disk is a standard ext4 filesystem, readable individually even if MergerFS or SnapRAID is removed
  • Failure isolation — if one disk dies, you only lose the data on that disk; SnapRAID parity allows recovery without the stress of a traditional RAID rebuild
  • Cost — can add disks one at a time as budget allows, no need for matched sets
  • Parity without real-time overhead — SnapRAID calculates parity on a schedule (not real-time), so there's no write performance penalty during normal use
  • Separation of concerns — MergerFS handles pooling, SnapRAID handles protection; each can be changed independently

Trade-offs accepted:

  • SnapRAID parity is snapshot-based (scheduled sync) — data written between syncs is unprotected
  • Performance is limited to single-disk speed per file (fine for media streaming)
  • Two tools to understand instead of one (ZFS does pooling + redundancy in a single solution)

🔗 MergerFS vs alternatives | SnapRAID vs RAID


Why Azure KeyVault over GitHub Secrets Alone?

Decision: Use Azure KeyVault as the source of truth, with GitHub Secrets only holding access credentials.

Reasoning:

  • Shared access — both repos pull from the same vault (no duplicating secrets)
  • Audit trail — KeyVault logs access events; GitHub Secrets don't
  • Rotation — update once in KeyVault, both repos pick it up next run
  • Scale — GitHub Secrets are limited per-environment / repo; KeyVault scales to hundreds
  • Separation — the "keys to the safe" (SPN creds) are separate from "what's in the safe"

Trade-offs accepted:

  • Azure dependency (but it's free tier eligible and extremely reliable)
  • Slightly more complex initial setup (one-time cost)

Why WireGuard over Tailscale or Cloudflare Tunnel?

Decision: Raw WireGuard tunnel from GitHub Actions runner to home network.

Reasoning:

  • No third-party dependency — no Tailscale account, no Cloudflare account
  • Deterministic — the tunnel config is static and self-contained in one secret
  • Speed — WireGuard connects in milliseconds, critical for CI/CD speed
  • Simplicity — one config file, one command (wg-quick up)

Trade-offs accepted:

  • Requires a static endpoint (or dynamic DNS) for the home WireGuard peer
  • Config is all-or-nothing (no granular ACLs like Tailscale offers)
  • No web UI for management

Why LVM Snapshots for Backups?

Decision: Use LVM snapshots to capture Docker data with minimal container downtime.

Reasoning:

  • Minimal downtime — containers are stopped only for the instant it takes to create the snapshot (seconds, not minutes)
  • Consistency — snapshot captures a point-in-time view; no torn writes
  • Already available — Ubuntu Server default install uses LVM, so no additional tooling
  • Simple — create snapshot, tar it, delete snapshot. No backup agent to install

Trade-offs accepted:

  • Snapshot space (100G) must be provisioned
  • If snapshot fills before backup completes, it's invalidated
  • Only protects /docker — OS is rebuilt from code, not backed up

Why Two Repos Instead of One?

Decision: Separate nas-as-code (OS provisioning) from nas-containers-as-code (Docker stacks).

Reasoning:

  • Different cadence — container stacks change frequently (new services, config tweaks); OS provisioning changes rarely
  • Different blast radius — a bad container deploy is a restart; a bad OS change could brick the NAS
  • Independent pipelines — containers can redeploy without re-running the full Ansible provisioning
  • Separation of concerns — OS team vs app team thinking (even if it's just me 😅)

Trade-offs accepted:

  • Cross-repo references for shared concerns (secrets, networking)
  • Two wiki locations to maintain
  • Slightly more complex initial setup

Why Not Terraform / Bicep for the NAS Itself?

Decision: Use Ansible (not Terraform/Bicep) for NAS configuration.

Reasoning:

  • Ansible is designed for configuration management — it excels at "make this server look like this"
  • Terraform is for infrastructure provisioning — creating cloud resources, not configuring bare-metal servers
  • Agentless — Ansible uses SSH, no agent to install on the NAS
  • Idempotent — run it repeatedly, same result
  • Mature ecosystem — modules for apt, systemd, files, mounts all built-in

The Azure KeyVault could be Terraform-managed, but since it's a one-time setup that rarely changes, the manual az commands were simpler.

Clone this wiki locally