Storage¶
This document covers the storage architecture, including the NFS dynamic provisioner, the shared media volume, per-application configuration volumes, and the NAS folder structure.
Storage Architecture¶
flowchart TD
subgraph nas["Unifi NAS"]
nfsExport["Media NFS Export\n(shared-data-pv.yml)"]
configExport["Config NFS Exports"]
end
subgraph k8sCluster["Kubernetes Cluster"]
nfsProv["NFS Subdir External Provisioner"]
localProv["Local-Path Provisioner"]
scNfsClient["StorageClass: nfs-client"]
scLocalPath["StorageClass: local-path"]
subgraph sharedVol["Shared Media Volume"]
arrPV["PV: arr-data (10Ti)"]
arrPVC["PVC: arr-data"]
end
subgraph nfsVols["NFS Config Volumes"]
prowlarrPVC["PVC: arr-prowlarr-config"]
otherPVC["PVC: ..."]
end
subgraph localVols["Local-Path Config Volumes"]
sonarrPVC["PVC: arr-sonarr-config"]
radarrPVC["PVC: arr-radarr-config"]
end
subgraph appPods["Application Pods"]
jellyfin["Jellyfin"]
sonarr["Sonarr"]
radarr["Radarr"]
tdarr["Tdarr"]
otherApps["..."]
end
end
nfsExport --> arrPV
configExport --> nfsProv
nfsProv --> scNfsClient
scNfsClient --> nfsVols
localProv --> scLocalPath
scLocalPath --> localVols
arrPVC --> appPods
nfsVols --> appPods
localVols --> appPods
Storage Provisioners¶
NFS Subdir External Provisioner¶
The NFS Subdir External Provisioner dynamically creates PersistentVolumes backed by subdirectories on the Unifi NAS. It eliminates the need to manually pre-create PVs for each application.
| Setting | Value |
|---|---|
| StorageClass Name | nfs-client |
| NFS Server | Unifi NAS |
| Path Pattern | ${.PVC.namespace}-${.PVC.name} |
| Reclaim Policy | Retain |
| Sync Wave | -2 |
The pathPattern creates predictable directory names on the NAS. For example, a PVC named arr-prowlarr-config in the arr namespace creates the NFS subdirectory arr-arr-prowlarr-config.
Default StorageClass
nfs-client serves as the default StorageClass for the cluster. Any PVC that does not specify a storageClassName will be provisioned by this provisioner.
Local-Path Provisioner¶
The Rancher Local-Path Provisioner provides node-local storage for applications that require proper POSIX file locking, such as those using SQLite databases. NFS does not support the file locking primitives that SQLite requires, which causes database lock contention and potential corruption.
| Setting | Value |
|---|---|
| StorageClass Name | local-path |
| Volume Binding Mode | WaitForFirstConsumer |
| Reclaim Policy | Retain |
| Storage Path | /opt/local-path-provisioner/ |
| Sync Wave | -2 |
Local-path volumes are tied to the worker node where they are first provisioned. Applications using this StorageClass (Sonarr, Radarr) define their PVCs as standalone kustomize resources with existingClaim references in their Helm values.
Node Affinity
Pods using local-path PVCs are pinned to the node where the volume was created. If the node becomes unavailable, the pod cannot reschedule to another node until the original node recovers.
Shared Media Volume (arr-data)¶
All media applications share a single 10Ti PersistentVolume backed by a dedicated NFS export on the Unifi NAS. This shared volume enables applications to access media files without copying data between volumes.
Volume Specification¶
| Property | Value |
|---|---|
| PV Name | arr-data |
| Capacity | 10Ti |
| Access Mode | ReadWriteMany |
| NFS Path | Environment-specific (configured in k8s/clusters/homelabk8s01/apps/arr/shared-data-pv.yml) |
| NFS Server | Unifi NAS (192.168.1.158) |
| PVC Name | arr-data |
| PVC Namespace | arr |
Each application mounts the shared PVC at /data within its pod, maintaining a consistent path structure that matches the NAS layout. This allows Sonarr, Radarr, and other apps to perform hardlinks and atomic moves instead of cross-device copies.
Hardlinks and Atomic Moves
Because all applications share the same underlying NFS mount, file operations like hardlinks and atomic moves work correctly. This is critical for the arr stack workflow where Sonarr/Radarr move completed downloads into the media library without duplicating data.
NAS Folder Structure¶
The NAS follows the recommended media server folder structure, keeping downloads and library content under a single /data root:
/data/
torrents/
movies/
tv/
music/
books/
media/
movies/
tv/
music/
books/
Path Mapping by Application¶
| Application | Mount Path | NAS Subdirectory Used |
|---|---|---|
| qBittorrent | /data/torrents |
Torrent download destination |
| Sonarr | /data |
Manages /data/media/tv, imports from /data/torrents/tv |
| Radarr | /data |
Manages /data/media/movies, imports from /data/torrents/movies |
| Bazarr | /data/media |
Reads media directories for subtitle matching |
| Jellyfin | /data/media |
Serves content from media library |
| Tdarr | /data/media |
Transcodes media files in-place |
Per-Application Config Volumes¶
Each application has its own PVC for configuration and database storage. Most use the nfs-client StorageClass. Applications with SQLite databases that are sensitive to NFS locking limitations use local-path instead.
| Application | PVC Name | StorageClass | Typical Size |
|---|---|---|---|
| Jellyfin | arr-jellyfin-config |
nfs-client |
10Gi - 50Gi |
| Sonarr | arr-sonarr-config |
local-path |
5Gi |
| Radarr | arr-radarr-config |
local-path |
5Gi |
| Prowlarr | arr-prowlarr-config |
nfs-client |
1Gi |
| Bazarr | arr-bazarr-config |
nfs-client |
1Gi |
| Seerr | arr-seerr-config |
nfs-client |
1Gi |
| Tdarr | arr-tdarr-config |
nfs-client |
1Gi |
Infrastructure Storage¶
Several infrastructure components also use nfs-client for persistent storage:
| Component | PVC | Size | Purpose |
|---|---|---|---|
| Prometheus | prometheus-data |
20Gi | Metrics time-series data (15d retention) |
| Loki | loki-data |
10Gi | Log storage (168h retention) |
| Grafana | grafana-data |
Persistent | Dashboards and data source configuration |
| MinIO | minio-data |
50Gi | S3 backup object storage |