From ee39dd2fc98aee468ce37e8c747282bda6730007 Mon Sep 17 00:00:00 2001 From: Viktor Barzin Date: Sat, 4 Apr 2026 16:26:59 +0300 Subject: [PATCH] feat(storage): migrate 12 SQLite NFS PVCs to proxmox-lvm (Wave 1) Add proxmox-lvm PVCs with pvc-autoresizer annotations for all SQLite-backed services. Deployments updated to use new block storage PVCs. Old NFS modules retained for 1-week rollback. Services: ntfy, freshrss, insta2spotify, actualbudget (x3), wealthfolio, navidrome (DB only), audiobookshelf config, headscale, forgejo, uptime-kuma. Also: set Recreate strategy on ntfy, forgejo, insta2spotify, wealthfolio (required for RWO volumes). --- stacks/actualbudget/factory/main.tf | 24 +++++++++- stacks/ebooks/main.tf | 24 +++++++++- stacks/forgejo/main.tf | 26 +++++++++- stacks/freshrss/main.tf | 24 +++++++++- stacks/headscale/modules/headscale/main.tf | 26 +++++++++- stacks/insta2spotify/main.tf | 27 ++++++++++- stacks/navidrome/main.tf | 47 ++++++++++++++++++- stacks/ntfy/main.tf | 26 +++++++++- .../uptime-kuma/modules/uptime-kuma/main.tf | 24 +++++++++- stacks/wealthfolio/main.tf | 29 +++++++++++- 10 files changed, 263 insertions(+), 14 deletions(-) diff --git a/stacks/actualbudget/factory/main.tf b/stacks/actualbudget/factory/main.tf index 32e8dcbc..9020922a 100644 --- a/stacks/actualbudget/factory/main.tf +++ b/stacks/actualbudget/factory/main.tf @@ -27,6 +27,28 @@ module "nfs_data" { nfs_path = "/mnt/main/actualbudget/${var.name}" } +resource "kubernetes_persistent_volume_claim" "data_proxmox" { + wait_until_bound = false + metadata { + name = "actualbudget-${var.name}-data-proxmox" + namespace = "actualbudget" + annotations = { + "resize.topolvm.io/threshold" = "80%" + "resize.topolvm.io/increase" = "100%" + "resize.topolvm.io/storage_limit" = "5Gi" + } + } + spec { + access_modes = ["ReadWriteOnce"] + storage_class_name = "proxmox-lvm" + resources { + requests = { + storage = "1Gi" + } + } + } +} + resource "kubernetes_deployment" "actualbudget" { metadata { name = "actualbudget-${var.name}" @@ -81,7 +103,7 @@ resource "kubernetes_deployment" "actualbudget" { volume { name = "data" persistent_volume_claim { - claim_name = module.nfs_data.claim_name + claim_name = kubernetes_persistent_volume_claim.data_proxmox.metadata[0].name } } } diff --git a/stacks/ebooks/main.tf b/stacks/ebooks/main.tf index c8dd006f..dd83303a 100644 --- a/stacks/ebooks/main.tf +++ b/stacks/ebooks/main.tf @@ -203,6 +203,28 @@ module "nfs_audiobookshelf_config" { nfs_path = "/mnt/main/audiobookshelf/config" } +resource "kubernetes_persistent_volume_claim" "abs_config_proxmox" { + wait_until_bound = false + metadata { + name = "ebooks-abs-config-proxmox" + namespace = kubernetes_namespace.ebooks.metadata[0].name + annotations = { + "resize.topolvm.io/threshold" = "80%" + "resize.topolvm.io/increase" = "100%" + "resize.topolvm.io/storage_limit" = "5Gi" + } + } + spec { + access_modes = ["ReadWriteOnce"] + storage_class_name = "proxmox-lvm" + resources { + requests = { + storage = "1Gi" + } + } + } +} + module "nfs_audiobookshelf_metadata" { source = "../../modules/kubernetes/nfs_volume" name = "ebooks-abs-metadata" @@ -589,7 +611,7 @@ resource "kubernetes_deployment" "audiobookshelf" { volume { name = "config" persistent_volume_claim { - claim_name = module.nfs_audiobookshelf_config.claim_name + claim_name = kubernetes_persistent_volume_claim.abs_config_proxmox.metadata[0].name } } volume { diff --git a/stacks/forgejo/main.tf b/stacks/forgejo/main.tf index 388ff006..f9547113 100644 --- a/stacks/forgejo/main.tf +++ b/stacks/forgejo/main.tf @@ -28,6 +28,28 @@ module "nfs_data" { nfs_path = "/mnt/main/forgejo" } +resource "kubernetes_persistent_volume_claim" "data_proxmox" { + wait_until_bound = false + metadata { + name = "forgejo-data-proxmox" + namespace = kubernetes_namespace.forgejo.metadata[0].name + annotations = { + "resize.topolvm.io/threshold" = "80%" + "resize.topolvm.io/increase" = "50%" + "resize.topolvm.io/storage_limit" = "20Gi" + } + } + spec { + access_modes = ["ReadWriteOnce"] + storage_class_name = "proxmox-lvm" + resources { + requests = { + storage = "5Gi" + } + } + } +} + resource "kubernetes_deployment" "forgejo" { metadata { name = "forgejo" @@ -40,7 +62,7 @@ resource "kubernetes_deployment" "forgejo" { spec { replicas = 1 strategy { - type = "RollingUpdate" # DB is external so we can roll + type = "Recreate" } selector { match_labels = { @@ -110,7 +132,7 @@ resource "kubernetes_deployment" "forgejo" { volume { name = "data" persistent_volume_claim { - claim_name = module.nfs_data.claim_name + claim_name = kubernetes_persistent_volume_claim.data_proxmox.metadata[0].name } } } diff --git a/stacks/freshrss/main.tf b/stacks/freshrss/main.tf index 4cef01dd..d9020851 100644 --- a/stacks/freshrss/main.tf +++ b/stacks/freshrss/main.tf @@ -65,6 +65,28 @@ module "nfs_data" { nfs_path = "/mnt/main/freshrss/data" } +resource "kubernetes_persistent_volume_claim" "data_proxmox" { + wait_until_bound = false + metadata { + name = "freshrss-data-proxmox" + namespace = kubernetes_namespace.immich.metadata[0].name + annotations = { + "resize.topolvm.io/threshold" = "80%" + "resize.topolvm.io/increase" = "100%" + "resize.topolvm.io/storage_limit" = "5Gi" + } + } + spec { + access_modes = ["ReadWriteOnce"] + storage_class_name = "proxmox-lvm" + resources { + requests = { + storage = "1Gi" + } + } + } +} + module "nfs_extensions" { source = "../../modules/kubernetes/nfs_volume" name = "freshrss-extensions" @@ -144,7 +166,7 @@ resource "kubernetes_deployment" "freshrss" { volume { name = "data" persistent_volume_claim { - claim_name = module.nfs_data.claim_name + claim_name = kubernetes_persistent_volume_claim.data_proxmox.metadata[0].name } } volume { diff --git a/stacks/headscale/modules/headscale/main.tf b/stacks/headscale/modules/headscale/main.tf index 305da387..e9796f73 100644 --- a/stacks/headscale/modules/headscale/main.tf +++ b/stacks/headscale/modules/headscale/main.tf @@ -44,6 +44,28 @@ module "nfs_data" { nfs_path = "/mnt/main/headscale" } +resource "kubernetes_persistent_volume_claim" "data_proxmox" { + wait_until_bound = false + metadata { + name = "headscale-data-proxmox" + namespace = kubernetes_namespace.headscale.metadata[0].name + annotations = { + "resize.topolvm.io/threshold" = "80%" + "resize.topolvm.io/increase" = "100%" + "resize.topolvm.io/storage_limit" = "5Gi" + } + } + spec { + access_modes = ["ReadWriteOnce"] + storage_class_name = "proxmox-lvm" + resources { + requests = { + storage = "1Gi" + } + } + } +} + resource "kubernetes_deployment" "headscale" { metadata { name = "headscale" @@ -164,7 +186,7 @@ resource "kubernetes_deployment" "headscale" { volume { name = "nfs-config" persistent_volume_claim { - claim_name = module.nfs_data.claim_name + claim_name = kubernetes_persistent_volume_claim.data_proxmox.metadata[0].name } } # container { @@ -414,7 +436,7 @@ resource "kubernetes_cron_job_v1" "headscale_backup" { volume { name = "nfs-data" persistent_volume_claim { - claim_name = module.nfs_data.claim_name + claim_name = kubernetes_persistent_volume_claim.data_proxmox.metadata[0].name } } restart_policy = "OnFailure" diff --git a/stacks/insta2spotify/main.tf b/stacks/insta2spotify/main.tf index fbb5a6dc..7dac71b1 100644 --- a/stacks/insta2spotify/main.tf +++ b/stacks/insta2spotify/main.tf @@ -49,6 +49,28 @@ module "nfs_data" { nfs_path = "/mnt/main/insta2spotify" } +resource "kubernetes_persistent_volume_claim" "data_proxmox" { + wait_until_bound = false + metadata { + name = "insta2spotify-data-proxmox" + namespace = kubernetes_namespace.insta2spotify.metadata[0].name + annotations = { + "resize.topolvm.io/threshold" = "80%" + "resize.topolvm.io/increase" = "100%" + "resize.topolvm.io/storage_limit" = "5Gi" + } + } + spec { + access_modes = ["ReadWriteOnce"] + storage_class_name = "proxmox-lvm" + resources { + requests = { + storage = "1Gi" + } + } + } +} + resource "kubernetes_deployment" "insta2spotify" { metadata { name = "insta2spotify" @@ -63,6 +85,9 @@ resource "kubernetes_deployment" "insta2spotify" { } spec { replicas = 1 + strategy { + type = "Recreate" + } selector { match_labels = { app = "insta2spotify" @@ -174,7 +199,7 @@ resource "kubernetes_deployment" "insta2spotify" { volume { name = "data" persistent_volume_claim { - claim_name = module.nfs_data.claim_name + claim_name = kubernetes_persistent_volume_claim.data_proxmox.metadata[0].name } } } diff --git a/stacks/navidrome/main.tf b/stacks/navidrome/main.tf index 1626b705..0b55731b 100644 --- a/stacks/navidrome/main.tf +++ b/stacks/navidrome/main.tf @@ -66,6 +66,28 @@ module "nfs_data" { nfs_path = "/mnt/main/navidrome" } +resource "kubernetes_persistent_volume_claim" "data_proxmox" { + wait_until_bound = false + metadata { + name = "navidrome-data-proxmox" + namespace = kubernetes_namespace.navidrome.metadata[0].name + annotations = { + "resize.topolvm.io/threshold" = "80%" + "resize.topolvm.io/increase" = "100%" + "resize.topolvm.io/storage_limit" = "5Gi" + } + } + spec { + access_modes = ["ReadWriteOnce"] + storage_class_name = "proxmox-lvm" + resources { + requests = { + storage = "1Gi" + } + } + } +} + module "nfs_music" { source = "../../modules/kubernetes/nfs_volume" name = "navidrome-music" @@ -82,6 +104,14 @@ module "nfs_lidarr" { nfs_path = "/mnt/main/servarr/lidarr" } +module "nfs_freedify" { + source = "../../modules/kubernetes/nfs_volume" + name = "navidrome-freedify" + namespace = kubernetes_namespace.navidrome.metadata[0].name + nfs_server = var.nfs_server + nfs_path = "/mnt/main/freedify-music" +} + resource "kubernetes_deployment" "navidrome" { metadata { name = "navidrome" @@ -125,6 +155,15 @@ resource "kubernetes_deployment" "navidrome" { mount_path = "/lidarr" read_only = true } + volume_mount { + name = "freedify" + mount_path = "/freedify-music" + read_only = true + } + env { + name = "ND_SCANSCHEDULE" + value = "0" + } port { name = "http" container_port = 4533 @@ -143,7 +182,7 @@ resource "kubernetes_deployment" "navidrome" { volume { name = "data" persistent_volume_claim { - claim_name = module.nfs_data.claim_name + claim_name = kubernetes_persistent_volume_claim.data_proxmox.metadata[0].name } } volume { @@ -158,6 +197,12 @@ resource "kubernetes_deployment" "navidrome" { claim_name = module.nfs_lidarr.claim_name } } + volume { + name = "freedify" + persistent_volume_claim { + claim_name = module.nfs_freedify.claim_name + } + } } } } diff --git a/stacks/ntfy/main.tf b/stacks/ntfy/main.tf index fdd43667..1d85b101 100644 --- a/stacks/ntfy/main.tf +++ b/stacks/ntfy/main.tf @@ -28,6 +28,28 @@ module "nfs_data" { nfs_path = "/mnt/main/ntfy" } +resource "kubernetes_persistent_volume_claim" "data_proxmox" { + wait_until_bound = false + metadata { + name = "ntfy-data-proxmox" + namespace = kubernetes_namespace.ntfy.metadata[0].name + annotations = { + "resize.topolvm.io/threshold" = "80%" + "resize.topolvm.io/increase" = "100%" + "resize.topolvm.io/storage_limit" = "5Gi" + } + } + spec { + access_modes = ["ReadWriteOnce"] + storage_class_name = "proxmox-lvm" + resources { + requests = { + storage = "1Gi" + } + } + } +} + resource "kubernetes_deployment" "ntfy" { metadata { name = "ntfy" @@ -43,7 +65,7 @@ resource "kubernetes_deployment" "ntfy" { spec { replicas = 1 strategy { - type = "RollingUpdate" + type = "Recreate" } selector { match_labels = { @@ -131,7 +153,7 @@ resource "kubernetes_deployment" "ntfy" { volume { name = "data" persistent_volume_claim { - claim_name = module.nfs_data.claim_name + claim_name = kubernetes_persistent_volume_claim.data_proxmox.metadata[0].name } } } diff --git a/stacks/uptime-kuma/modules/uptime-kuma/main.tf b/stacks/uptime-kuma/modules/uptime-kuma/main.tf index 7215015f..74a33783 100644 --- a/stacks/uptime-kuma/modules/uptime-kuma/main.tf +++ b/stacks/uptime-kuma/modules/uptime-kuma/main.tf @@ -28,6 +28,28 @@ module "nfs_data" { nfs_path = "/mnt/main/uptime-kuma" } +resource "kubernetes_persistent_volume_claim" "data_proxmox" { + wait_until_bound = false + metadata { + name = "uptime-kuma-data-proxmox" + namespace = kubernetes_namespace.uptime-kuma.metadata[0].name + annotations = { + "resize.topolvm.io/threshold" = "80%" + "resize.topolvm.io/increase" = "50%" + "resize.topolvm.io/storage_limit" = "20Gi" + } + } + spec { + access_modes = ["ReadWriteOnce"] + storage_class_name = "proxmox-lvm" + resources { + requests = { + storage = "5Gi" + } + } + } +} + resource "kubernetes_deployment" "uptime-kuma" { metadata { name = "uptime-kuma" @@ -106,7 +128,7 @@ resource "kubernetes_deployment" "uptime-kuma" { volume { name = "data" persistent_volume_claim { - claim_name = module.nfs_data.claim_name + claim_name = kubernetes_persistent_volume_claim.data_proxmox.metadata[0].name } } dns_config { diff --git a/stacks/wealthfolio/main.tf b/stacks/wealthfolio/main.tf index 335c9d44..ce2dc55a 100644 --- a/stacks/wealthfolio/main.tf +++ b/stacks/wealthfolio/main.tf @@ -60,6 +60,28 @@ module "nfs_data" { nfs_path = "/mnt/main/wealthfolio" } +resource "kubernetes_persistent_volume_claim" "data_proxmox" { + wait_until_bound = false + metadata { + name = "wealthfolio-data-proxmox" + namespace = kubernetes_namespace.wealthfolio.metadata[0].name + annotations = { + "resize.topolvm.io/threshold" = "80%" + "resize.topolvm.io/increase" = "100%" + "resize.topolvm.io/storage_limit" = "5Gi" + } + } + spec { + access_modes = ["ReadWriteOnce"] + storage_class_name = "proxmox-lvm" + resources { + requests = { + storage = "1Gi" + } + } + } +} + resource "kubernetes_deployment" "wealthfolio" { lifecycle { ignore_changes = [spec[0].template[0].spec[0].dns_config] @@ -77,6 +99,9 @@ resource "kubernetes_deployment" "wealthfolio" { } spec { replicas = 1 + strategy { + type = "Recreate" + } selector { match_labels = { app = "wealthfolio" @@ -141,7 +166,7 @@ resource "kubernetes_deployment" "wealthfolio" { volume { name = "data" persistent_volume_claim { - claim_name = module.nfs_data.claim_name + claim_name = kubernetes_persistent_volume_claim.data_proxmox.metadata[0].name } } } @@ -276,7 +301,7 @@ resource "kubernetes_cron_job_v1" "wealthfolio_sync" { volume { name = "data" persistent_volume_claim { - claim_name = module.nfs_data.claim_name + claim_name = kubernetes_persistent_volume_claim.data_proxmox.metadata[0].name } } }