diff --git a/.gitignore b/.gitignore
index 217032a..c56a9cf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,3 @@
 .direnv
 .pre-commit-config.yaml
 result
-.manifests
diff --git a/README.md b/README.md
index d6888cd..19e76c3 100644
--- a/README.md
+++ b/README.md
@@ -5,82 +5,74 @@ We use [Kubenix](https://kubenix.org/) to write Kubernetes deployments in Nix!
 ## Images used
 
 Legend:
-
-- ✨: Image built with Nix (including
-  [NixNG](https://github.com/nix-community/NixNG))
+- ✨: Image built with Nix (including [NixNG](https://github.com/nix-community/NixNG))
 - ✅: Official image or trusted publisher
 - 🫤: Unofficial image
 
-| Status | Image                                          | Comments                                                  |
-| ------ | ---------------------------------------------- | --------------------------------------------------------- |
-| ✨     | `nixng-blog`                                   |                                                           |
-| ✨     | `nixng-dnsmasq`                                |                                                           |
-| ✨     | `nixng-attic`                                  |                                                           |
-| ✨     | `nixng-ntfy-sh`                                |                                                           |
-| ✨     | `nixng-radicale`                               |                                                           |
-| ✨     | `nixng-jellyseerr`                             |                                                           |
-| ✨     | `nixng-radarr`                                 |                                                           |
-| ✨     | `nixng-sonarr`                                 |                                                           |
-| ✨     | `nixng-bazarr`                                 |                                                           |
-| ✨     | `nixng-prowlarr`                               |                                                           |
-| ✨     | `nixng-deluge`                                 |                                                           |
-| ✨     | `nixng-mealie`                                 |                                                           |
-| ✨     | `nixng-atuin`                                  |                                                           |
-| ✅     | `jellyfin/jellyfin`                            |                                                           |
-| ✅     | `postgres:14`                                  | Database for Atuin                                        |
-| ✅     | `ghcr.io/paperless-ngx/paperless-ngx`          |                                                           |
-| ✅     | `docker.io/library/redis:7`                    | Database for Paperless-ngx                                |
-| ✅     | `nextcloud`                                    |                                                           |
-| ✅     | `postgres:15`                                  | Database for Attic, Nextcloud, Paperless-ngx and Hedgedoc |
-| ✅     | `inbucket/inbucket`                            |                                                           |
-| ✅     | `lscr.io/linuxserver/syncthing`                |                                                           |
-| ✅     | `codeberg.org/forgejo/forgejo`                 |                                                           |
-| ✅     | `ghcr.io/immich-app/immich-server`             |                                                           |
-| ✅     | `ghcr.io/immich-app/immich-machine-learning`   |                                                           |
-| ✅     | `docker.io/redis:6.2-alpine`                   | Database for Immich                                       |
-| ✅     | `docker.io/tensorchord/pgvecto-rs:pg14-v0.2.0` | Database for Immich                                       |
-| ✅     | `tombursch/kitchenowl`                         |                                                           |
-| ✅     | `freshrss/freshrss`                            |                                                           |
-| ✅     | `ubuntu/bind9`                                 |                                                           |
-| ✅     | `quay.io/hedgedoc/hedgedoc`                    |                                                           |
-| 🫤     | `teddysun/kms`                                 |                                                           |
-| 🫤     | `mpepping/cyberchef`                           |                                                           |
+| Status   | Image    | Comments       |
+| ---    | ---   | ---     |
+| ✨ | `nixng-blog` | |
+| ✨ | `nixng-dnsmasq` | |
+| ✨ | `nixng-attic` | |
+| ✨ | `nixng-ntfy-sh` | |
+| ✨ | `nixng-radicale` | |
+| ✨ | `nixng-jellyseerr` | |
+| ✨ | `nixng-radarr` | |
+| ✨ | `nixng-sonarr` | |
+| ✨ | `nixng-bazarr` | |
+| ✨ | `nixng-prowlarr` | |
+| ✅ | `jellyfin/jellyfin` | |
+| ✅ | `linuxserver/deluge` | |
+| ✅ | `ghcr.io/atuinsh/atuin` | |
+| ✅ | `postgres:14` | Database for Atuin |
+| ✅ | `ghcr.io/paperless-ngx/paperless-ngx` | |
+| ✅ | `docker.io/library/redis:7` | Database for Paperless-ngx |
+| ✅ | `nextcloud` | |
+| ✅ | `postgres:15` | Database for Attic, Nextcloud, Paperless-ngx and Hedgedoc |
+| ✅ | `inbucket/inbucket` | |
+| ✅ | `lscr.io/linuxserver/syncthing` | |
+| ✅ | `codeberg.org/forgejo/forgejo` | |
+| ✅ | `pihole/pihole` | |
+| ✅ | `ghcr.io/immich-app/immich-server` | |
+| ✅ | `ghcr.io/immich-app/immich-machine-learning` | |
+| ✅ | `docker.io/redis:6.2-alpine` | Database for Immich |
+| ✅ | `docker.io/tensorchord/pgvecto-rs:pg14-v0.2.0` | Database for Immich |
+| ✅ | `tombursch/kitchenowl` | |
+| ✅ | `freshrss/freshrss` | |
+| ✅ | `ubuntu/bind9` | |
+| ✅ | `quay.io/hedgedoc/hedgedoc` | |
+| 🫤 | `itzg/minecraft-server` | |
+| 🫤 | `teddysun/kms` | |
+| 🫤 | `mpepping/cyberchef` | |
 
 ## Acknowledgements
 
-- [dns.nix](https://github.com/kirelagin/dns.nix): A Nix DSL for defining DNS
-  zones
-- [flake-utils](https://github.com/numtide/flake-utils): Handy utilities to
-  develop Nix flakes
-- [kubenix](https://kubenix.org/): Declare and deploy Kubernetes resources using
-  Nix
+- [dns.nix](https://github.com/kirelagin/dns.nix): A Nix DSL for defining DNS zones
+- [flake-utils](https://github.com/numtide/flake-utils): Handy utilities to develop Nix flakes
+- [kubenix](https://kubenix.org/): Declare and deploy Kubernetes resources using Nix
 - [nixhelm](https://github.com/farcaller/nixhelm): Nix-digestible Helm charts
 - [sops-nix](https://github.com/Mic92/sops-nix): Sops secret management for Nix
 
 ## Prerequisites
 
-To deploy to the Kubernetes cluster, first make sure you have an admin account
-on the cluster. You can generate this using
-`nix run '.#gen-k3s-cert' <username> <servername> ~/.kube`, assuming you have
-SSH access to the master node. This puts a private key, signed certificate and a
-kubeconfig in the kubeconfig directory
+To deploy to the Kubernetes cluster, first make sure you have an admin account on the cluster.
+You can generate this using `nix run '.#gen-k3s-cert' <username> <servername> ~/.kube`, assuming you have SSH access to the master node.
+This puts a private key, signed certificate and a kubeconfig in the kubeconfig directory
 
 ## Bootstrapping
 
-We are now ready to deploy to the Kubernetes cluster. Deployments are done
-through an experimental Kubernetes feature called
-[ApplySets](https://kubernetes.io/docs/tasks/manage-kubernetes-objects/declarative-config/#how-to-delete-objects).
+We are now ready to deploy to the Kubernetes cluster.
+Deployments are done through an experimental Kubernetes feature called [ApplySets](https://kubernetes.io/docs/tasks/manage-kubernetes-objects/declarative-config/#how-to-delete-objects).
 Each applyset is responsible for a set number of resources within a namespace.
 
-If the cluster has not been initialized yet, we must bootstrap it first. Run
-these deployments:
-
+If the cluster has not been initialized yet, we must bootstrap it first.
+Run these deployments:
 - `nix run '.#bootstrap-default-deploy'`
 - `nix run '.#bootstrap-kube-system-deploy'`
 
 ## Deployment
 
-Now the cluster has been initialized and we can deploy applications. To explore
-which applications we can deploy, run `nix flake show`. Then, for each
-application, run `nix run '.#<application>-deploy'`. Or, if you're lazy:
-`nix flake show --json | jq -r '.packages."x86_64-linux"|keys[]' | grep -- -deploy | xargs -I{} nix run ".#{}"`.
+Now the cluster has been initialized and we can deploy applications.
+To explore which applications we can deploy, run `nix flake show`.
+Then, for each application, run `nix run '.#<application>-deploy'`.
+Or, if you're lazy: `nix flake show --json | jq -r '.packages."x86_64-linux"|keys[]' | grep -- -deploy | xargs -I{} nix run ".#{}"`.
diff --git a/applyset-deploy.sh b/applyset-deploy.sh
index f7ec8e7..81bc8dc 100644
--- a/applyset-deploy.sh
+++ b/applyset-deploy.sh
@@ -2,31 +2,11 @@
 
 set -euo pipefail
 
-CREATE_LOCAL_GCROOT=false
-
-while [[ "$#" -gt 0 ]]; do
-	case "$1" in
-	--help)
-		echo "Use --create-local-gcroot to create local GC root"
-		exit 0
-		;;
-	--create-local-gcroot)
-		CREATE_LOCAL_GCROOT=true
-		shift
-		;;
-	*)
-		echo "Unknown option: $1"
-		exit 1
-		;;
-	esac
-done
-
 first_server="${SERVERS%% *}"
-previous_manifest=$(
-	envsubst <<EOF | ssh -T "root@$first_server"
-        if [[ -f "$GCROOTDIR/$NAME.yml" ]]; then
-                cat "$GCROOTDIR/$NAME.yml"
-        fi
+previous_manifest=$(ssh -T "root@$first_server" << EOF
+if [[ -f "$GCROOTDIR/${NAME}.yml" ]]; then
+	cat "$GCROOTDIR/${NAME}.yml"
+fi
 EOF
 )
 
@@ -34,35 +14,30 @@ set +e
 if [ -z "$previous_manifest" ]; then
 	echo No previous manifest found!
 else
-	$DYFF between <(echo "$previous_manifest") "$MANIFEST" \
+	$DYFF between <(echo $previous_manifest) $MANIFEST \
 		--exclude-regexp metadata.labels.kubenix/hash \
 		--exclude-regexp labels.kubenix/hash \
-		--set-exit-code
+		--set-exit-code 
+
+	if [ $? -eq 0 ]; then
+		exit 0
+	fi
 fi
 set -e
 
-read -r -p "Continue? " _
+read -r -p "Continue? " response
 
 echo Uploading closure...
 for server in $SERVERS; do
-	echo Uploading closure to "$server"...
-	nix copy --to "ssh://root@$server" "$MANIFEST"
+	echo Uploading closure to $server...
+	nix copy --to "ssh://root@$server.dmz" $MANIFEST
+	ssh "root@$server.dmz" "mkdir -p $GCROOTDIR && ln -sf $MANIFEST $GCROOTDIR/${NAME}.yml"
 done
 
 echo Applying Kubernetes manifest...
 export KUBECTL_APPLYSET=true
-vals eval -fail-on-missing-key-in-map <"$MANIFEST" |
-	kubectl apply -f - \
-		--prune \
-		--applyset applyset-"$NAME" \
-		--namespace "$NAMESPACE"
-
-echo Creating GC roots
-for server in $SERVERS; do
-	ssh "root@$server" "mkdir -p $GCROOTDIR && ln -sf $MANIFEST $GCROOTDIR/${NAME}.yml"
-done
-
-if $CREATE_LOCAL_GCROOT; then
-	mkdir -p ./.manifests
-	ln -sf "$MANIFEST" "./.manifests/${NAME}.yml"
-fi
+vals eval -fail-on-missing-key-in-map <$MANIFEST | \
+kubectl apply -f - \
+	--prune \
+	--applyset applyset-$NAME \
+	--namespace $NAMESPACE
diff --git a/deployments.nix b/deployments.nix
index 0764427..eb0433d 100644
--- a/deployments.nix
+++ b/deployments.nix
@@ -69,6 +69,11 @@
     namespace = "syncthing";
   };
 
+  pihole = {
+    module.pihole.enable = true;
+    namespace = "dns";
+  };
+
   immich = {
     module.immich.enable = true;
     namespace = "immich";
@@ -104,6 +109,11 @@
     namespace = "kube-system";
   };
 
+  minecraft = {
+    module.minecraft.enable = true;
+    namespace = "minecraft";
+  };
+
   tailscale = {
     module.tailscale.enable = true;
     namespace = "tailscale";
@@ -113,14 +123,4 @@
     module.ntfy.enable = true;
     namespace = "ntfy";
   };
-
-  authentik = {
-    module.authentik.enable = true;
-    namespace = "authentik";
-  };
-
-  mealie = {
-    module.mealie.enable = true;
-    namespace = "mealie";
-  };
 }
diff --git a/docs/longhorn.md b/docs/longhorn.md
new file mode 100644
index 0000000..3230543
--- /dev/null
+++ b/docs/longhorn.md
@@ -0,0 +1,98 @@
+# Longhorn notes
+
+## Troubleshooting
+
+```
+Multi-Attach error for volume "prowlarr" Volume is already exclusively attached to one node and can't be attached to another
+```
+
+I solved the above problem like this:
+```
+❯ kubectl get volumeattachments | grep prowlarr
+csi-f13ee1f46a4acc0d7e4abe8a3c993c7e043e9a55cd7573bda3499085654b493a   driver.longhorn.io   prowlarr                                   lewis   true       3m38s
+❯ kubectl delete volumeattachments csi-f13ee1f46a4acc0d7e4abe8a3c993c7e043e9a55cd7573bda3499085654b493a
+❯ kubectl rollout restart -n media deployment prowlarr
+```
+
+```
+driver name driver.longhorn.io not found in the list of registered CSI drivers
+```
+
+I solved this by restarting k3s:
+```
+systemctl restart k3s
+```
+
+## Migration from NFS to Longhorn
+
+1. Delete the workload, and delete the PVC and PVC using NFS.
+2. Create Longhorn volumes as described below.
+3. Copy NFS data from lewis.dmz to local disk.
+4. Spin up a temporary pod and mount the Longhorn volume(s) in it:
+   ```nix
+    {
+      pods.testje.spec = {
+        containers.testje = {
+          image = "nginx";
+  
+          volumeMounts = [
+            {
+              name = "uploads";
+              mountPath = "/hedgedoc/public/uploads";
+            }
+          ];
+        };
+  
+        volumes = {
+          uploads.persistentVolumeClaim.claimName = "hedgedoc-uploads";
+        };
+      };
+    }
+   ```
+5. Use `kubectl cp` to copy the data from the local disk to the pod.
+6. Delete the temporary pod.
+7. Be sure to set the group ownership of the mount to the correct GID.
+7. Create the workload with updated volume mounts.
+8. Delete the data from local disk.
+
+## Creation of new Longhorn volumes
+
+While it seems handy to use a K8s StorageClass for Longhorn, we do *not* want to use that.
+If you use a StorageClass, a PV and Longhorn volume will be automatically provisioned.
+These will have the name `pvc-<UID of PVC>`, where the UID of the PVC is random.
+This makes it hard to restore a backup to a Longhorn volume with the correct name.
+
+Instead, we want to manually create the Longhorn volumes via the web UI.
+Then, we can create the PV and PVC as usual using our K8s provisioning tool (e.g. Kubectl/Kubenix).
+
+Follow these actions to create a Volume:
+1. Using the Longhorn web UI, create a new Longhorn volume, keeping the following in mind:
+     - The size can be some more than what we expect to reasonable use. We use storage-overprovisioning, so the total size of volumes can exceed real disk size.
+     - The number of replicas should be 2.
+2. Enable the "backup-nfs" recurring job for the Longhorn volume.
+3. Disable the "default" recurring job group for the Longhorn volume.
+4. Create the PV, PVC and workload as usual.
+
+## Disaster recovery using Longhorn backups
+
+Backing up Longhorn volumes is very easy, but restoring them is more tricky.
+We consider here the case when all our machines are wiped, and all we have left is Longhorn backups.
+To restore a backup, perform the following actions:
+1. Restore the latest snapshot in the relevant Longhorn backup, keeping the following in mind:
+   - The name should remain the same (i.e. the one chosen at Longhorn volume creation).
+   - The number of replicas should be 2.
+   - Disable recurring jobs.
+2. Enable the "backup-nfs" recurring job for the Longhorn volume.
+3. Disable the "default" recurring job group for the Longhorn volume.
+4. Create the PV, PVC and workload as usual.
+
+## Recovering Longhorn volumes without a Kubernetes cluster
+
+1. Navigate to the Longhorn backupstore location (`/mnt/longhorn/persistent/longhorn-backup/backupstore/volumes` for us).
+2. Find the directory for the desired volume: `ls **/**`.
+3. Determine the last backup for the volume: `cat volume.cfg | jq '.LastBackupName'`.
+4. Find the blocks and the order that form the volume: `cat backups/<name>.cfg | jq '.Blocks'`.
+5. Extract each block using lz4: `lz4 -d blocks/XX/YY/XXYY.blk block`.
+6. Append the blocks to form the file system: `cat block1 block2 block3 > volume.img`
+7. Lastly we need to fix the size of the image. We can simply append zero's to the end until the file is long enough so `fsck.ext4` does not complain anymore.
+8. Mount the image: `mount -o loop volume.img /mnt/volume`.
diff --git a/flake.lock b/flake.lock
index ce49b38..6ad2f9b 100644
--- a/flake.lock
+++ b/flake.lock
@@ -6,11 +6,11 @@
         "nixpkgs": "nixpkgs"
       },
       "locked": {
-        "lastModified": 1749306247,
-        "narHash": "sha256-4IKXo51HPvYB8t3FC7QBhQZGXANCgMrEvdgvBjv2SIk=",
+        "lastModified": 1735508994,
+        "narHash": "sha256-SMMX3irZ4Y+0QEAq0mOYEnJIYRe3YnXHrkCSRvdxHxU=",
         "ref": "refs/heads/master",
-        "rev": "ba47dfab6c5e8f3b91db61789cfd4b7b88ce6b02",
-        "revCount": 27,
+        "rev": "433c1ef4b5874e2c4782be7322604d17182035ab",
+        "revCount": 23,
         "type": "git",
         "url": "https://git.kun.is/pim/blog"
       },
@@ -19,6 +19,47 @@
         "url": "https://git.kun.is/pim/blog"
       }
     },
+    "deploy-rs": {
+      "inputs": {
+        "flake-compat": "flake-compat_4",
+        "nixpkgs": "nixpkgs_4",
+        "utils": "utils"
+      },
+      "locked": {
+        "lastModified": 1727447169,
+        "narHash": "sha256-3KyjMPUKHkiWhwR91J1YchF6zb6gvckCAY1jOE+ne0U=",
+        "owner": "serokell",
+        "repo": "deploy-rs",
+        "rev": "aa07eb05537d4cd025e2310397a6adcedfe72c76",
+        "type": "github"
+      },
+      "original": {
+        "owner": "serokell",
+        "repo": "deploy-rs",
+        "type": "github"
+      }
+    },
+    "disko": {
+      "inputs": {
+        "nixpkgs": [
+          "servers",
+          "nixpkgs"
+        ]
+      },
+      "locked": {
+        "lastModified": 1729712798,
+        "narHash": "sha256-a+Aakkb+amHw4biOZ0iMo8xYl37uUL48YEXIC5PYJ/8=",
+        "owner": "nix-community",
+        "repo": "disko",
+        "rev": "09a776702b004fdf9c41a024e1299d575ee18a7d",
+        "type": "github"
+      },
+      "original": {
+        "owner": "nix-community",
+        "repo": "disko",
+        "type": "github"
+      }
+    },
     "dns": {
       "inputs": {
         "flake-utils": "flake-utils",
@@ -27,11 +68,33 @@
         ]
       },
       "locked": {
-        "lastModified": 1737653493,
-        "narHash": "sha256-qTbv8Pm9WWF63M5Fj0Od9E54/lsbMSQUBHw/s30eFok=",
+        "lastModified": 1733919067,
+        "narHash": "sha256-ZsL5pKwEDhcZhVJh+3IwgHus7kSW/N8qOlBscwB6BCI=",
         "owner": "kirelagin",
         "repo": "dns.nix",
-        "rev": "96e548ae8bd44883afc5bddb9dacd0502542276d",
+        "rev": "a23f43f9762aa96d3e35c8eeefa7610bd0cdf456",
+        "type": "github"
+      },
+      "original": {
+        "owner": "kirelagin",
+        "repo": "dns.nix",
+        "type": "github"
+      }
+    },
+    "dns_2": {
+      "inputs": {
+        "flake-utils": "flake-utils_5",
+        "nixpkgs": [
+          "servers",
+          "nixpkgs"
+        ]
+      },
+      "locked": {
+        "lastModified": 1726867691,
+        "narHash": "sha256-IK3r16N9pizf53AipOmrcrcyjVsPJwC4PI5hIqEyKwQ=",
+        "owner": "kirelagin",
+        "repo": "dns.nix",
+        "rev": "a3196708a56dee76186a9415c187473b94e6cbae",
         "type": "github"
       },
       "original": {
@@ -88,6 +151,70 @@
         "type": "github"
       }
     },
+    "flake-compat_4": {
+      "flake": false,
+      "locked": {
+        "lastModified": 1696426674,
+        "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
+        "owner": "edolstra",
+        "repo": "flake-compat",
+        "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
+        "type": "github"
+      },
+      "original": {
+        "owner": "edolstra",
+        "repo": "flake-compat",
+        "type": "github"
+      }
+    },
+    "flake-compat_5": {
+      "flake": false,
+      "locked": {
+        "lastModified": 1696426674,
+        "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
+        "owner": "edolstra",
+        "repo": "flake-compat",
+        "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
+        "type": "github"
+      },
+      "original": {
+        "owner": "edolstra",
+        "repo": "flake-compat",
+        "type": "github"
+      }
+    },
+    "flake-compat_6": {
+      "flake": false,
+      "locked": {
+        "lastModified": 1673956053,
+        "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=",
+        "owner": "edolstra",
+        "repo": "flake-compat",
+        "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9",
+        "type": "github"
+      },
+      "original": {
+        "owner": "edolstra",
+        "repo": "flake-compat",
+        "type": "github"
+      }
+    },
+    "flake-compat_7": {
+      "flake": false,
+      "locked": {
+        "lastModified": 1696426674,
+        "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
+        "owner": "edolstra",
+        "repo": "flake-compat",
+        "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
+        "type": "github"
+      },
+      "original": {
+        "owner": "edolstra",
+        "repo": "flake-compat",
+        "type": "github"
+      }
+    },
     "flake-parts": {
       "inputs": {
         "nixpkgs-lib": [
@@ -109,6 +236,28 @@
         "type": "github"
       }
     },
+    "flake-parts_2": {
+      "inputs": {
+        "nixpkgs-lib": [
+          "servers",
+          "nix-snapshotter",
+          "nixpkgs"
+        ]
+      },
+      "locked": {
+        "lastModified": 1704152458,
+        "narHash": "sha256-DS+dGw7SKygIWf9w4eNBUZsK+4Ug27NwEWmn2tnbycg=",
+        "owner": "hercules-ci",
+        "repo": "flake-parts",
+        "rev": "88a2cd8166694ba0b6cb374700799cec53aef527",
+        "type": "github"
+      },
+      "original": {
+        "owner": "hercules-ci",
+        "repo": "flake-parts",
+        "type": "github"
+      }
+    },
     "flake-utils": {
       "locked": {
         "lastModified": 1614513358,
@@ -147,11 +296,11 @@
         "systems": "systems_4"
       },
       "locked": {
-        "lastModified": 1731533236,
-        "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
+        "lastModified": 1710146030,
+        "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
         "owner": "numtide",
         "repo": "flake-utils",
-        "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
+        "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
         "type": "github"
       },
       "original": {
@@ -163,6 +312,39 @@
       "inputs": {
         "systems": "systems_5"
       },
+      "locked": {
+        "lastModified": 1710146030,
+        "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
+        "type": "github"
+      },
+      "original": {
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "type": "github"
+      }
+    },
+    "flake-utils_5": {
+      "locked": {
+        "lastModified": 1614513358,
+        "narHash": "sha256-LakhOx3S1dRjnh0b5Dg3mbZyH0ToC9I8Y2wKSkBaTzU=",
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "rev": "5466c5bbece17adaab2d82fae80b46e807611bf3",
+        "type": "github"
+      },
+      "original": {
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "type": "github"
+      }
+    },
+    "flake-utils_6": {
+      "inputs": {
+        "systems": "systems_8"
+      },
       "locked": {
         "lastModified": 1726560853,
         "narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=",
@@ -201,14 +383,39 @@
         "gitignore": "gitignore",
         "nixpkgs": [
           "nixpkgs"
-        ]
+        ],
+        "nixpkgs-stable": "nixpkgs-stable"
       },
       "locked": {
-        "lastModified": 1747372754,
-        "narHash": "sha256-2Y53NGIX2vxfie1rOW0Qb86vjRZ7ngizoo+bnXU9D9k=",
+        "lastModified": 1734797603,
+        "narHash": "sha256-ulZN7ps8nBV31SE+dwkDvKIzvN6hroRY8sYOT0w+E28=",
         "owner": "cachix",
         "repo": "git-hooks.nix",
-        "rev": "80479b6ec16fefd9c1db3ea13aeb038c60530f46",
+        "rev": "f0f0dc4920a903c3e08f5bdb9246bb572fcae498",
+        "type": "github"
+      },
+      "original": {
+        "owner": "cachix",
+        "repo": "git-hooks.nix",
+        "type": "github"
+      }
+    },
+    "git-hooks_2": {
+      "inputs": {
+        "flake-compat": "flake-compat_5",
+        "gitignore": "gitignore_2",
+        "nixpkgs": [
+          "servers",
+          "nixpkgs-unstable"
+        ],
+        "nixpkgs-stable": "nixpkgs-stable_2"
+      },
+      "locked": {
+        "lastModified": 1730302582,
+        "narHash": "sha256-W1MIJpADXQCgosJZT8qBYLRuZls2KSiKdpnTVdKBuvU=",
+        "owner": "cachix",
+        "repo": "git-hooks.nix",
+        "rev": "af8a16fe5c264f5e9e18bcee2859b40a656876cf",
         "type": "github"
       },
       "original": {
@@ -238,6 +445,28 @@
         "type": "github"
       }
     },
+    "gitignore_2": {
+      "inputs": {
+        "nixpkgs": [
+          "servers",
+          "git-hooks",
+          "nixpkgs"
+        ]
+      },
+      "locked": {
+        "lastModified": 1709087332,
+        "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
+        "owner": "hercules-ci",
+        "repo": "gitignore.nix",
+        "rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
+        "type": "github"
+      },
+      "original": {
+        "owner": "hercules-ci",
+        "repo": "gitignore.nix",
+        "type": "github"
+      }
+    },
     "globset": {
       "inputs": {
         "nixpkgs-lib": [
@@ -304,14 +533,38 @@
         "type": "github"
       }
     },
+    "kubenix_2": {
+      "inputs": {
+        "flake-compat": "flake-compat_6",
+        "nixpkgs": [
+          "servers",
+          "nixpkgs-unstable"
+        ],
+        "systems": "systems_9",
+        "treefmt": "treefmt_2"
+      },
+      "locked": {
+        "lastModified": 1717788185,
+        "narHash": "sha256-Uc6QSQqJa2lyv/1W4StwoKrjtq7cFjlKNhdrtanToGo=",
+        "owner": "pizzapim",
+        "repo": "kubenix",
+        "rev": "a9590abe23a2f7577bc3271d90955e9ccc2923fe",
+        "type": "github"
+      },
+      "original": {
+        "owner": "pizzapim",
+        "repo": "kubenix",
+        "type": "github"
+      }
+    },
     "nginx": {
       "flake": false,
       "locked": {
-        "lastModified": 1748296742,
-        "narHash": "sha256-E67DlHlrwomNSnRdjcnyOGhACtFbMz2jwm/glxeOxbg=",
+        "lastModified": 1735301654,
+        "narHash": "sha256-PHcSyHYyPUwPAls0BgtnGu2e936vhxW2nt7bQxDyGAQ=",
         "owner": "nginx",
         "repo": "nginx",
-        "rev": "5b8a5c08ce28639e788734b2528faad70baa113c",
+        "rev": "e3a9b6ad08a86e799a3d77da3f2fc507d3c9699e",
         "type": "github"
       },
       "original": {
@@ -329,11 +582,11 @@
         ]
       },
       "locked": {
-        "lastModified": 1729742964,
-        "narHash": "sha256-B4mzTcQ0FZHdpeWcpDYPERtyjJd/NIuaQ9+BV1h+MpA=",
+        "lastModified": 1703863825,
+        "narHash": "sha256-rXwqjtwiGKJheXB43ybM8NwWB8rO2dSRrEqes0S7F5Y=",
         "owner": "nix-community",
         "repo": "nix-github-actions",
-        "rev": "e04df33f62cdcf93d73e9a04142464753a16db67",
+        "rev": "5163432afc817cf8bd1f031418d1869e4c9d5547",
         "type": "github"
       },
       "original": {
@@ -344,11 +597,11 @@
     },
     "nix-kube-generators": {
       "locked": {
-        "lastModified": 1729269463,
-        "narHash": "sha256-8jDDpC99fYl5CSHjZyPwb5PK7nQSknhkpfe8+DXI910=",
+        "lastModified": 1708155396,
+        "narHash": "sha256-A/BIeJjiRS7sBYP6tFJa/WHDPHe7DGTCkSEKXttYeAQ=",
         "owner": "farcaller",
         "repo": "nix-kube-generators",
-        "rev": "2be4f3cb99e179d9f94e6c8723862421437f8efb",
+        "rev": "14dbd5e5b40615937900f71d9a9851b59b4d9a88",
         "type": "github"
       },
       "original": {
@@ -380,6 +633,29 @@
         "type": "github"
       }
     },
+    "nix-snapshotter_2": {
+      "inputs": {
+        "flake-compat": "flake-compat_7",
+        "flake-parts": "flake-parts_2",
+        "nixpkgs": [
+          "servers",
+          "nixpkgs-unstable"
+        ]
+      },
+      "locked": {
+        "lastModified": 1729627456,
+        "narHash": "sha256-TCZdXCmnqCPsd3PjLv/LDSKJhTspLliL0DE+c/XP9BY=",
+        "owner": "pdtpartners",
+        "repo": "nix-snapshotter",
+        "rev": "f2957822a3748c91e678657a1cfd009b0440bbfd",
+        "type": "github"
+      },
+      "original": {
+        "owner": "pdtpartners",
+        "repo": "nix-snapshotter",
+        "type": "github"
+      }
+    },
     "nixhelm": {
       "inputs": {
         "flake-utils": "flake-utils_3",
@@ -391,11 +667,11 @@
         "poetry2nix": "poetry2nix"
       },
       "locked": {
-        "lastModified": 1748482293,
-        "narHash": "sha256-Q3lyAFjJeR2d5kmiiSmb8ni121siv/ByjD2ueg2hUQA=",
+        "lastModified": 1735607967,
+        "narHash": "sha256-hdOdhQskvxyPPrf4w/k484xfVEVsqktHjwS0noTRRCw=",
         "owner": "farcaller",
         "repo": "nixhelm",
-        "rev": "0ecf1ebc80e65ff8bc37e8c852cac6cc149aea6e",
+        "rev": "6ce9cfd0e06bbf609af333069b3c4e84cd739755",
         "type": "github"
       },
       "original": {
@@ -412,19 +688,73 @@
         "treefmt-nix": "treefmt-nix_2"
       },
       "locked": {
-        "lastModified": 1746371274,
-        "narHash": "sha256-7X65+TP0luFpQsA6KV80R05qnWp7NxMaIDryFfJ4MqI=",
-        "owner": "nix-community",
+        "lastModified": 1735726395,
+        "narHash": "sha256-rwhsZuwJzJ825Et7YI73G7+wHiPLFfx3SOnozWZfLJ0=",
+        "owner": "pizzapim",
         "repo": "NixNG",
-        "rev": "ea02c5dd1f2e80ee7acc47f75ec3439ed480dd6f",
+        "rev": "dbbbb22d8feed064455a8653a0450b3da5a31424",
         "type": "github"
       },
       "original": {
-        "owner": "nix-community",
+        "owner": "pizzapim",
+        "ref": "kubernetes",
         "repo": "NixNG",
         "type": "github"
       }
     },
+    "nixng_2": {
+      "inputs": {
+        "nixpkgs": [
+          "servers",
+          "nixpkgs"
+        ]
+      },
+      "locked": {
+        "lastModified": 1726571270,
+        "narHash": "sha256-LEug48WOL+mmFYtKM57e/oudgjBk2Km5zIP3p27hF8I=",
+        "owner": "pizzapim",
+        "repo": "NixNG",
+        "rev": "9538892da603608f0176d07d33b1265e038c0adf",
+        "type": "github"
+      },
+      "original": {
+        "owner": "pizzapim",
+        "ref": "dnsmasq",
+        "repo": "NixNG",
+        "type": "github"
+      }
+    },
+    "nixos-facter-modules": {
+      "locked": {
+        "lastModified": 1730737399,
+        "narHash": "sha256-PzJrTMhHb9f46uMxmRD4GjnyVuNqxeyEvxaq7OierUQ=",
+        "owner": "numtide",
+        "repo": "nixos-facter-modules",
+        "rev": "c22b916f629fee6941a2976c62247b0bec68082b",
+        "type": "github"
+      },
+      "original": {
+        "owner": "numtide",
+        "repo": "nixos-facter-modules",
+        "type": "github"
+      }
+    },
+    "nixos-hardware": {
+      "locked": {
+        "lastModified": 1729742320,
+        "narHash": "sha256-u3Of8xRkN//me8PU+RucKA59/6RNy4B2jcGAF36P4jI=",
+        "owner": "NixOS",
+        "repo": "nixos-hardware",
+        "rev": "e8a2f6d5513fe7b7d15701b2d05404ffdc3b6dda",
+        "type": "github"
+      },
+      "original": {
+        "owner": "NixOS",
+        "ref": "master",
+        "repo": "nixos-hardware",
+        "type": "github"
+      }
+    },
     "nixpkgs": {
       "locked": {
         "lastModified": 1714076141,
@@ -441,13 +771,45 @@
         "type": "github"
       }
     },
+    "nixpkgs-bazarr": {
+      "locked": {
+        "lastModified": 1735086895,
+        "narHash": "sha256-893hOoQn5t9g0r57N0D8/G5WC4pPaNlprjAYO0TWRxc=",
+        "owner": "r-ryantm",
+        "repo": "nixpkgs",
+        "rev": "89e79d58769436a8cfd0d80ae28012d51134f2f3",
+        "type": "github"
+      },
+      "original": {
+        "owner": "r-ryantm",
+        "ref": "auto-update/bazarr",
+        "repo": "nixpkgs",
+        "type": "github"
+      }
+    },
+    "nixpkgs-jellyseerr": {
+      "locked": {
+        "lastModified": 1735406088,
+        "narHash": "sha256-Cwah5iXhOJ3cbrPYG5oeSyhQ7F7BsAabHTeezD4elh0=",
+        "owner": "coonce",
+        "repo": "nixpkgs",
+        "rev": "25569750ccc0c692128e667a77585d6d27ff7e57",
+        "type": "github"
+      },
+      "original": {
+        "owner": "coonce",
+        "ref": "jellyseerr",
+        "repo": "nixpkgs",
+        "type": "github"
+      }
+    },
     "nixpkgs-master": {
       "locked": {
-        "lastModified": 1748523262,
-        "narHash": "sha256-QGBY1wNWqtrT/9LYPcrie4e7EC44/CXGljkM2N/puWg=",
+        "lastModified": 1735935963,
+        "narHash": "sha256-i6xTJb3sb4BeWypD/DjAmslDzGXZUGU1OFJliaKFuuc=",
         "owner": "nixos",
         "repo": "nixpkgs",
-        "rev": "9e0df1205d7c2acb4504a25091ee603fd1dd9865",
+        "rev": "20166d17f391d2e11311baaa74344381fa44e4a0",
         "type": "github"
       },
       "original": {
@@ -457,6 +819,86 @@
         "type": "github"
       }
     },
+    "nixpkgs-radicale": {
+      "locked": {
+        "lastModified": 1735496163,
+        "narHash": "sha256-oqUP98g0eqfzCDA/i88qRIBq4BIyxEk9um7dfNGiw+I=",
+        "owner": "erictapen",
+        "repo": "nixpkgs",
+        "rev": "e14050d0c94dc929543f7e4502fda8539d36536f",
+        "type": "github"
+      },
+      "original": {
+        "owner": "erictapen",
+        "ref": "radicale",
+        "repo": "nixpkgs",
+        "type": "github"
+      }
+    },
+    "nixpkgs-stable": {
+      "locked": {
+        "lastModified": 1730741070,
+        "narHash": "sha256-edm8WG19kWozJ/GqyYx2VjW99EdhjKwbY3ZwdlPAAlo=",
+        "owner": "NixOS",
+        "repo": "nixpkgs",
+        "rev": "d063c1dd113c91ab27959ba540c0d9753409edf3",
+        "type": "github"
+      },
+      "original": {
+        "owner": "NixOS",
+        "ref": "nixos-24.05",
+        "repo": "nixpkgs",
+        "type": "github"
+      }
+    },
+    "nixpkgs-stable_2": {
+      "locked": {
+        "lastModified": 1720386169,
+        "narHash": "sha256-NGKVY4PjzwAa4upkGtAMz1npHGoRzWotlSnVlqI40mo=",
+        "owner": "NixOS",
+        "repo": "nixpkgs",
+        "rev": "194846768975b7ad2c4988bdb82572c00222c0d7",
+        "type": "github"
+      },
+      "original": {
+        "owner": "NixOS",
+        "ref": "nixos-24.05",
+        "repo": "nixpkgs",
+        "type": "github"
+      }
+    },
+    "nixpkgs-stable_3": {
+      "locked": {
+        "lastModified": 1729357638,
+        "narHash": "sha256-66RHecx+zohbZwJVEPF7uuwHeqf8rykZTMCTqIrOew4=",
+        "owner": "NixOS",
+        "repo": "nixpkgs",
+        "rev": "bb8c2cf7ea0dd2e18a52746b2c3a5b0c73b93c22",
+        "type": "github"
+      },
+      "original": {
+        "owner": "NixOS",
+        "ref": "release-24.05",
+        "repo": "nixpkgs",
+        "type": "github"
+      }
+    },
+    "nixpkgs-unstable": {
+      "locked": {
+        "lastModified": 1729818716,
+        "narHash": "sha256-XRfkUsxLzFkMn3Tpstio1gNOIQ+2PZPCKbifJ2IXxlw=",
+        "owner": "NixOS",
+        "repo": "nixpkgs",
+        "rev": "062c4f59744fcffa2e5aa3ef443dc8b4d1674ed6",
+        "type": "github"
+      },
+      "original": {
+        "owner": "NixOS",
+        "ref": "nixpkgs-unstable",
+        "repo": "nixpkgs",
+        "type": "github"
+      }
+    },
     "nixpkgs_2": {
       "locked": {
         "lastModified": 1726871744,
@@ -475,11 +917,11 @@
     },
     "nixpkgs_3": {
       "locked": {
-        "lastModified": 1748370509,
-        "narHash": "sha256-QlL8slIgc16W5UaI3w7xHQEP+Qmv/6vSNTpoZrrSlbk=",
+        "lastModified": 1735471104,
+        "narHash": "sha256-0q9NGQySwDQc7RhAV2ukfnu7Gxa5/ybJ2ANT8DQrQrs=",
         "owner": "nixos",
         "repo": "nixpkgs",
-        "rev": "4faa5f5321320e49a78ae7848582f684d64783e9",
+        "rev": "88195a94f390381c6afcdaa933c2f6ff93959cb4",
         "type": "github"
       },
       "original": {
@@ -491,11 +933,43 @@
     },
     "nixpkgs_4": {
       "locked": {
-        "lastModified": 1747958103,
-        "narHash": "sha256-qmmFCrfBwSHoWw7cVK4Aj+fns+c54EBP8cGqp/yK410=",
+        "lastModified": 1702272962,
+        "narHash": "sha256-D+zHwkwPc6oYQ4G3A1HuadopqRwUY/JkMwHz1YF7j4Q=",
+        "owner": "NixOS",
+        "repo": "nixpkgs",
+        "rev": "e97b3e4186bcadf0ef1b6be22b8558eab1cdeb5d",
+        "type": "github"
+      },
+      "original": {
+        "owner": "NixOS",
+        "ref": "nixpkgs-unstable",
+        "repo": "nixpkgs",
+        "type": "github"
+      }
+    },
+    "nixpkgs_5": {
+      "locked": {
+        "lastModified": 1726871744,
+        "narHash": "sha256-V5LpfdHyQkUF7RfOaDPrZDP+oqz88lTJrMT1+stXNwo=",
         "owner": "nixos",
         "repo": "nixpkgs",
-        "rev": "fe51d34885f7b5e3e7b59572796e1bcb427eccb1",
+        "rev": "a1d92660c6b3b7c26fb883500a80ea9d33321be2",
+        "type": "github"
+      },
+      "original": {
+        "owner": "nixos",
+        "ref": "nixpkgs-unstable",
+        "repo": "nixpkgs",
+        "type": "github"
+      }
+    },
+    "nixpkgs_6": {
+      "locked": {
+        "lastModified": 1733097829,
+        "narHash": "sha256-9hbb1rqGelllb4kVUCZ307G2k3/UhmA8PPGBoyuWaSw=",
+        "owner": "nixos",
+        "repo": "nixpkgs",
+        "rev": "2c15aa59df0017ca140d9ba302412298ab4bf22a",
         "type": "github"
       },
       "original": {
@@ -517,11 +991,11 @@
         "treefmt-nix": "treefmt-nix"
       },
       "locked": {
-        "lastModified": 1738741221,
-        "narHash": "sha256-UiTOA89yQV5YNlO1ZAp4IqJUGWOnTyBC83netvt8rQE=",
+        "lastModified": 1718285706,
+        "narHash": "sha256-DScsBM+kZvxOva7QegfdtleebMXh30XPxDQr/1IGKYo=",
         "owner": "nix-community",
         "repo": "poetry2nix",
-        "rev": "be1fe795035d3d36359ca9135b26dcc5321b31fb",
+        "rev": "a5be1bbbe0af0266147a88e0ec43b18c722f2bb9",
         "type": "github"
       },
       "original": {
@@ -542,8 +1016,67 @@
         "nixhelm": "nixhelm",
         "nixng": "nixng",
         "nixpkgs": "nixpkgs_3",
+        "nixpkgs-bazarr": "nixpkgs-bazarr",
+        "nixpkgs-jellyseerr": "nixpkgs-jellyseerr",
         "nixpkgs-master": "nixpkgs-master",
+        "nixpkgs-radicale": "nixpkgs-radicale",
+        "servers": "servers",
+        "treefmt-nix": "treefmt-nix_4"
+      }
+    },
+    "servers": {
+      "inputs": {
+        "deploy-rs": "deploy-rs",
+        "disko": "disko",
+        "dns": "dns_2",
+        "flake-utils": "flake-utils_6",
+        "git-hooks": "git-hooks_2",
+        "kubenix": "kubenix_2",
+        "nix-snapshotter": "nix-snapshotter_2",
+        "nixng": "nixng_2",
+        "nixos-facter-modules": "nixos-facter-modules",
+        "nixos-hardware": "nixos-hardware",
+        "nixpkgs": [
+          "nixpkgs"
+        ],
+        "nixpkgs-unstable": "nixpkgs-unstable",
+        "sops-nix": "sops-nix",
         "treefmt-nix": "treefmt-nix_3"
+      },
+      "locked": {
+        "lastModified": 1733068232,
+        "narHash": "sha256-iZJ/cq07OVk2TQy6UV9JaXgLARQqJedmuPIHTtgVeeo=",
+        "ref": "refs/heads/master",
+        "rev": "68b79e086c4cc6b850ba12c60f3a978d18bd41b1",
+        "revCount": 495,
+        "type": "git",
+        "url": "https://git.kun.is/home/nixos-servers"
+      },
+      "original": {
+        "type": "git",
+        "url": "https://git.kun.is/home/nixos-servers"
+      }
+    },
+    "sops-nix": {
+      "inputs": {
+        "nixpkgs": [
+          "servers",
+          "nixpkgs"
+        ],
+        "nixpkgs-stable": "nixpkgs-stable_3"
+      },
+      "locked": {
+        "lastModified": 1729775275,
+        "narHash": "sha256-J2vtHq9sw1wWm0aTMXpEEAzsVCUMZDTEe5kiBYccpLE=",
+        "owner": "Mic92",
+        "repo": "sops-nix",
+        "rev": "78a0e634fc8981d6b564f08b6715c69a755c4c7d",
+        "type": "github"
+      },
+      "original": {
+        "owner": "Mic92",
+        "repo": "sops-nix",
+        "type": "github"
       }
     },
     "systems": {
@@ -621,6 +1154,20 @@
       }
     },
     "systems_6": {
+      "locked": {
+        "lastModified": 1681028828,
+        "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
+        "owner": "nix-systems",
+        "repo": "default",
+        "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
+        "type": "github"
+      },
+      "original": {
+        "id": "systems",
+        "type": "indirect"
+      }
+    },
+    "systems_7": {
       "locked": {
         "lastModified": 1681028828,
         "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
@@ -635,6 +1182,35 @@
         "type": "github"
       }
     },
+    "systems_8": {
+      "locked": {
+        "lastModified": 1681028828,
+        "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
+        "owner": "nix-systems",
+        "repo": "default",
+        "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
+        "type": "github"
+      },
+      "original": {
+        "owner": "nix-systems",
+        "repo": "default",
+        "type": "github"
+      }
+    },
+    "systems_9": {
+      "locked": {
+        "lastModified": 1681028828,
+        "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
+        "owner": "nix-systems",
+        "repo": "default",
+        "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
+        "type": "github"
+      },
+      "original": {
+        "id": "systems",
+        "type": "indirect"
+      }
+    },
     "treefmt": {
       "inputs": {
         "nixpkgs": [
@@ -665,11 +1241,11 @@
         ]
       },
       "locked": {
-        "lastModified": 1730120726,
-        "narHash": "sha256-LqHYIxMrl/1p3/kvm2ir925tZ8DkI0KA10djk8wecSk=",
+        "lastModified": 1717850719,
+        "narHash": "sha256-npYqVg+Wk4oxnWrnVG7416fpfrlRhp/lQ6wQ4DHI8YE=",
         "owner": "numtide",
         "repo": "treefmt-nix",
-        "rev": "9ef337e492a5555d8e17a51c911ff1f02635be15",
+        "rev": "4fc1c45a5f50169f9f29f6a98a438fb910b834ed",
         "type": "github"
       },
       "original": {
@@ -698,14 +1274,14 @@
     },
     "treefmt-nix_3": {
       "inputs": {
-        "nixpkgs": "nixpkgs_4"
+        "nixpkgs": "nixpkgs_5"
       },
       "locked": {
-        "lastModified": 1748243702,
-        "narHash": "sha256-9YzfeN8CB6SzNPyPm2XjRRqSixDopTapaRsnTpXUEY8=",
+        "lastModified": 1730025913,
+        "narHash": "sha256-Y9NtFmP8ciLyRsopcCx1tyoaaStKeq+EndwtGCgww7I=",
         "owner": "numtide",
         "repo": "treefmt-nix",
-        "rev": "1f3f7b784643d488ba4bf315638b2b0a4c5fb007",
+        "rev": "bae131e525cc8718da22fbeb8d8c7c43c4ea502a",
         "type": "github"
       },
       "original": {
@@ -713,6 +1289,64 @@
         "repo": "treefmt-nix",
         "type": "github"
       }
+    },
+    "treefmt-nix_4": {
+      "inputs": {
+        "nixpkgs": "nixpkgs_6"
+      },
+      "locked": {
+        "lastModified": 1735649548,
+        "narHash": "sha256-/4pTzlmABhx26AOTYvFN1OTCxJJL/LBUB49giqoMhJA=",
+        "owner": "numtide",
+        "repo": "treefmt-nix",
+        "rev": "597705118f16d1dcd0fef99707700d13b2b324d7",
+        "type": "github"
+      },
+      "original": {
+        "owner": "numtide",
+        "repo": "treefmt-nix",
+        "type": "github"
+      }
+    },
+    "treefmt_2": {
+      "inputs": {
+        "nixpkgs": [
+          "servers",
+          "kubenix",
+          "nixpkgs"
+        ]
+      },
+      "locked": {
+        "lastModified": 1688026376,
+        "narHash": "sha256-qJmkr9BWDpqblk4E9/rCsAEl39y2n4Ycw6KRopvpUcY=",
+        "owner": "numtide",
+        "repo": "treefmt-nix",
+        "rev": "df3f32b0cc253dfc7009b7317e8f0e7ccd70b1cf",
+        "type": "github"
+      },
+      "original": {
+        "owner": "numtide",
+        "repo": "treefmt-nix",
+        "type": "github"
+      }
+    },
+    "utils": {
+      "inputs": {
+        "systems": "systems_7"
+      },
+      "locked": {
+        "lastModified": 1701680307,
+        "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=",
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "rev": "4022d587cbbfd70fe950c1e2083a02621806a725",
+        "type": "github"
+      },
+      "original": {
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "type": "github"
+      }
     }
   },
   "root": "root",
diff --git a/flake.nix b/flake.nix
index 179a05c..c875d9f 100644
--- a/flake.nix
+++ b/flake.nix
@@ -7,6 +7,9 @@
     flake-utils.url = "github:numtide/flake-utils";
     treefmt-nix.url = "github:numtide/treefmt-nix";
     blog.url = "git+https://git.kun.is/pim/blog";
+    nixpkgs-jellyseerr.url = "github:coonce/nixpkgs?ref=jellyseerr";
+    nixpkgs-bazarr.url = "github:r-ryantm/nixpkgs?ref=auto-update/bazarr";
+    nixpkgs-radicale.url = "github:erictapen/nixpkgs?ref=radicale";
 
     git-hooks = {
       url = "github:cachix/git-hooks.nix";
@@ -28,8 +31,13 @@
       inputs.nixpkgs.follows = "nixpkgs";
     };
 
+    servers = {
+      url = "git+https://git.kun.is/home/nixos-servers";
+      inputs.nixpkgs.follows = "nixpkgs";
+    };
+
     nixng = {
-      url = "github:nix-community/NixNG";
+      url = "github:pizzapim/NixNG/kubernetes";
       inputs.nixpkgs.follows = "nixpkgs";
     };
 
@@ -52,6 +60,5 @@
       ./formatter.nix
       ./shell.nix
       ./nixng-configurations
-      ./nixng-modules
     ];
 }
diff --git a/globals.nix b/globals.nix
index a28687b..40081c5 100644
--- a/globals.nix
+++ b/globals.nix
@@ -1,25 +1,29 @@
-_: {
+{servers, ...}: let
   globals = {
     images = {
-      jellyfin = "jellyfin/jellyfin:10.10.7";
+      jellyfin = "jellyfin/jellyfin:10.10.3";
+      deluge = "linuxserver/deluge:2.1.1";
+      atuin = "ghcr.io/atuinsh/atuin:18.3.0";
       postgres14 = "postgres:14";
       kms = "teddysun/kms:latest";
-      paperless = "ghcr.io/paperless-ngx/paperless-ngx:2.16.2";
+      paperless = "ghcr.io/paperless-ngx/paperless-ngx:2.13.5";
       redis7 = "docker.io/library/redis:7";
-      nextcloud = "nextcloud:30.0.6";
+      nextcloud = "nextcloud:30.0.2";
       postgres15 = "postgres:15";
       inbucket = "inbucket/inbucket:edge";
-      syncthing = "lscr.io/linuxserver/syncthing:1.29.6";
-      forgejo = "codeberg.org/forgejo/forgejo:11.0.1";
-      immich = "ghcr.io/immich-app/immich-server:v1.134.0";
-      immich-machine-learning = "ghcr.io/immich-app/immich-machine-learning:v1.134.0";
-      immich-redis = "docker.io/valkey/valkey:8-bookworm@sha256:42cba146593a5ea9a622002c1b7cba5da7be248650cbb64ecb9c6c33d29794b1";
-      immich-postgres = "ghcr.io/immich-app/postgres:14-vectorchord0.3.0-pgvectors0.2.0";
-      kitchenowl = "tombursch/kitchenowl:v0.6.15";
+      syncthing = "lscr.io/linuxserver/syncthing:1.28.0";
+      forgejo = "codeberg.org/forgejo/forgejo:9.0.2";
+      pihole = "pihole/pihole:2024.07.0";
+      immich = "ghcr.io/immich-app/immich-server:v1.122.3";
+      immich-machine-learning = "ghcr.io/immich-app/immich-machine-learning:v1.122.3";
+      immich-redis = "docker.io/redis:6.2-alpine@sha256:eaba718fecd1196d88533de7ba49bf903ad33664a92debb24660a922ecd9cac8";
+      immich-postgres = "docker.io/tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:90724186f0a3517cf6914295b5ab410db9ce23190a2d9d0b9dd6463e3fa298f0";
+      kitchenowl = "tombursch/kitchenowl:v0.6.4";
       cyberchef = "mpepping/cyberchef:latest";
-      freshrss = "freshrss/freshrss:1.26.3";
+      freshrss = "freshrss/freshrss:1.24.3";
       bind9 = "ubuntu/bind9:9.18-22.04_beta";
-      hedgedoc = "quay.io/hedgedoc/hedgedoc:1.10.3";
+      hedgedoc = "quay.io/hedgedoc/hedgedoc:1.10.0";
+      minecraft = "itzg/minecraft-server:latest";
     };
 
     nodeLabels = {
@@ -31,28 +35,7 @@ _: {
         hasMedia = "true";
       };
     };
-
-    routerPublicIPv4 = "89.220.7.89";
-    bind9Ipv6 = "2a0d:6e00:1a77:30::134";
-
-    # Load balancer IPv4
-    traefikIPv4 = "192.168.30.128";
-    kmsIPv4 = "192.168.30.129";
-    inbucketIPv4 = "192.168.30.130";
-    gitIPv4 = "192.168.30.132";
-    delugeIPv4 = "192.168.30.133";
-    bind9IPv4 = "192.168.30.134";
-    dnsmasqIPv4 = "192.168.30.135";
-    jellyseerrIPv4 = "192.168.30.137";
-    syncthingIPv4 = "192.168.30.138";
-    radarrIPv4 = "192.168.30.140";
-    prowlarrIPv4 = "192.168.30.141";
-    sonarrIPv4 = "192.168.30.142";
-    bazarrIPv4 = "192.168.30.143";
-    paperlessIPv4 = "192.168.30.144";
-    radicaleIPv4 = "192.168.30.145";
-    freshrssIPv4 = "192.168.30.146";
-    immichIPv4 = "192.168.30.147";
-    nextcloudIPv4 = "192.168.30.148";
   };
+in {
+  globals = globals // servers.globals;
 }
diff --git a/kubenix.nix b/kubenix.nix
index 6f55e05..64806b8 100644
--- a/kubenix.nix
+++ b/kubenix.nix
@@ -8,7 +8,7 @@ inputs @ {
 flake-utils.lib.eachDefaultSystem
 (system: let
   pkgs = nixpkgs.legacyPackages.${system};
-  inherit (pkgs) lib;
+  lib = pkgs.lib;
   deployScript = (pkgs.writeScriptBin "applyset-deploy.sh" (builtins.readFile ./applyset-deploy.sh)).overrideAttrs (old: {
     buildCommand = "${old.buildCommand}\npatchShebangs $out";
   });
@@ -64,7 +64,7 @@ flake-utils.lib.eachDefaultSystem
       pkgs.symlinkJoin
       {
         name = "applyset-deploy.sh";
-        paths = [deployScript pkgs.vals pkgs.kubectl pkgs.gettext];
+        paths = [deployScript pkgs.vals pkgs.kubectl];
         buildInputs = [pkgs.makeWrapper];
         passthru.manifest = result;
         meta.mainProgram = "applyset-deploy.sh";
diff --git a/modules/attic.nix b/modules/attic.nix
index b67ace0..2742b7b 100644
--- a/modules/attic.nix
+++ b/modules/attic.nix
@@ -1,4 +1,5 @@
 {
+  self,
   utils,
   lib,
   config,
@@ -40,8 +41,6 @@
             };
 
             spec = {
-              nodeName = "jefke";
-
               containers.attic = {
                 image = utils.mkNixNGImage "attic";
                 ports.web.containerPort = 8080;
@@ -67,12 +66,8 @@
               };
 
               volumes = {
+                data.persistentVolumeClaim.claimName = "data";
                 server.secret.secretName = "server";
-
-                data.hostPath = {
-                  path = "/mnt/longhorn/persistent/volumes/attic";
-                  type = "Directory";
-                };
               };
 
               securityContext = {
@@ -96,8 +91,6 @@
             };
 
             spec = {
-              nodeName = "jefke";
-
               containers.postgres = {
                 image = globals.images.postgres15;
                 imagePullPolicy = "IfNotPresent";
@@ -122,10 +115,7 @@
                 ];
               };
 
-              volumes.data.hostPath = {
-                path = "/mnt/longhorn/persistent/volumes/attic-db";
-                type = "Directory";
-              };
+              volumes.data.persistentVolumeClaim.claimName = "database";
             };
           };
         };
@@ -167,6 +157,18 @@
           portName = "web";
         };
       };
+
+      longhorn.persistentVolumeClaim = {
+        data = {
+          volumeName = "attic";
+          storage = "15Gi";
+        };
+
+        database = {
+          volumeName = "attic-db";
+          storage = "150Mi";
+        };
+      };
     };
   };
 }
diff --git a/modules/atuin.nix b/modules/atuin.nix
index 685ae56..7485f9f 100644
--- a/modules/atuin.nix
+++ b/modules/atuin.nix
@@ -1,6 +1,5 @@
 {
   config,
-  utils,
   globals,
   lib,
   ...
@@ -30,22 +29,35 @@
           metadata.labels.app = "atuin";
 
           spec = {
-            nodeName = "jefke";
-
-            volumes.database.hostPath = {
-              path = "/mnt/longhorn/persistent/volumes/atuin-db";
-              type = "Directory";
+            volumes = {
+              data.persistentVolumeClaim.claimName = "data";
+              database.persistentVolumeClaim.claimName = "database";
             };
 
             containers = {
               atuin = {
-                image = utils.mkNixNGImage "atuin";
+                image = globals.images.atuin;
+                imagePullPolicy = "IfNotPresent";
                 ports.web.containerPort = 8888;
+                args = ["server" "start"];
 
-                env.ATUIN_DB_URI.valueFrom.secretKeyRef = {
-                  name = "database";
-                  key = "databaseURL";
+                env = {
+                  ATUIN_HOST.value = "0.0.0.0";
+                  ATUIN_PORT.value = "8888";
+                  ATUIN_OPEN_REGISTRATION.value = "false";
+
+                  ATUIN_DB_URI.valueFrom.secretKeyRef = {
+                    name = "database";
+                    key = "databaseURL";
+                  };
                 };
+
+                volumeMounts = [
+                  {
+                    name = "data";
+                    mountPath = "/config";
+                  }
+                ];
               };
 
               database = {
@@ -93,6 +105,18 @@
           portName = "web";
         };
       };
+
+      longhorn.persistentVolumeClaim = {
+        data = {
+          volumeName = "atuin";
+          storage = "300Mi";
+        };
+
+        database = {
+          volumeName = "atuin-db";
+          storage = "300Mi";
+        };
+      };
     };
   };
 }
diff --git a/modules/authentik.nix b/modules/authentik.nix
deleted file mode 100644
index 5737238..0000000
--- a/modules/authentik.nix
+++ /dev/null
@@ -1,112 +0,0 @@
-{
-  nixhelm,
-  system,
-  config,
-  lib,
-  ...
-}: {
-  options.authentik.enable = lib.mkEnableOption "authentik";
-
-  config = lib.mkIf config.authentik.enable {
-    kubernetes = {
-      helm.releases.authentik = {
-        chart = nixhelm.chartsDerivations.${system}.authentik.authentik;
-        includeCRDs = true;
-        namespace = "authentik";
-
-        values = {
-          authentik = {
-            email = {
-              host = "mail.smtp2go.com";
-              port = 2525;
-              from = "Authentik authentik@kun.is";
-            };
-          };
-
-          postgresql = {
-            enabled = true;
-            auth.password = "ref+sops://secrets.yml#/authentik/postgresql_password";
-            primary = {
-              persistence.enabled = false;
-              extraEnvVarsSecret = "postgresql-env";
-              extraVolumes = [
-                {
-                  name = "data";
-
-                  hostPath = {
-                    path = "/mnt/longhorn/persistent/volumes/authentik-db";
-                    type = "Directory";
-                  };
-                }
-              ];
-            };
-          };
-
-          redis = {
-            enabled = true;
-            master = {
-              persistence.enabled = false;
-              extraVolumes = [
-                {
-                  name = "authentik-redis";
-                  hostPath = {
-                    path = "/mnt/longhorn/persistent/volumes/authentik-redis";
-                    type = "Directory";
-                  };
-                }
-              ];
-              extraVolumeMounts = [
-                {
-                  mountPath = "/data";
-                  name = "authentik-redis";
-                }
-              ];
-            };
-          };
-        };
-      };
-
-      resources = let
-        env = {
-          AUTHENTIK_POSTGRESQL__PASSWORD.value = "ref+sops://secrets.yml#/authentik/postgresql_password";
-          AUTHENTIK_SECRET_KEY.value = "ref+sops://secrets.yml#/authentik/secret_key";
-          AUTHENTIK_EMAIL__USERNAME.value = "ref+sops://secrets.yml#/smtp2go/username";
-          AUTHENTIK_EMAIL__PASSWORD.value = "ref+sops://secrets.yml#/smtp2go/password";
-        };
-      in {
-        secrets.postgresql-env.stringData = {
-          POSTGRES_PASSWORD = "ref+sops://secrets.yml#/authentik/postgresql_password";
-        };
-
-        deployments = {
-          authentik-server.spec.template.spec.containers.server.env = env;
-          authentik-worker.spec.template.spec.containers.worker.env = env;
-        };
-
-        statefulSets.authentik-postgresql.spec.template.spec.nodeName = "atlas";
-        statefulSets.authentik-redis-master.spec.template.spec.nodeName = "atlas";
-      };
-    };
-
-    lab = {
-      ingresses.authentik = {
-        host = "authentik.kun.is";
-
-        service = {
-          name = "authentik-server";
-          portName = "http";
-        };
-      };
-
-      tailscaleIngresses = {
-        tailscale-authentik = {
-          host = "authentik";
-          service = {
-            name = "authentik-server";
-            portName = "http";
-          };
-        };
-      };
-    };
-  };
-}
diff --git a/modules/bind9/default.nix b/modules/bind9/default.nix
index 68d4025..5cebe2a 100644
--- a/modules/bind9/default.nix
+++ b/modules/bind9/default.nix
@@ -117,7 +117,6 @@ in {
         bind9-udp = {
           metadata.annotations = {
             "metallb.universe.tf/loadBalancerIPs" = "${globals.bind9IPv4},${globals.bind9Ipv6}";
-            # "metallb.universe.tf/loadBalancerIPs" = "${globals.bind9IPv4}";
             "metallb.universe.tf/allow-shared-ip" = "dns";
           };
 
@@ -138,7 +137,6 @@ in {
         bind9-tcp = {
           metadata.annotations = {
             "metallb.universe.tf/loadBalancerIPs" = "${globals.bind9IPv4},${globals.bind9Ipv6}";
-            # "metallb.universe.tf/loadBalancerIPs" = "${globals.bind9IPv4}";
             "metallb.universe.tf/allow-shared-ip" = "dns";
           };
 
diff --git a/modules/bind9/kun.is.zone.nix b/modules/bind9/kun.is.zone.nix
index 00999e8..3c12587 100644
--- a/modules/bind9/kun.is.zone.nix
+++ b/modules/bind9/kun.is.zone.nix
@@ -5,7 +5,7 @@ with dns.lib.combinators; {
   SOA = {
     nameServer = "ns1";
     adminEmail = "webmaster.kun.is";
-    serial = 2024041302;
+    serial = 2024041301;
   };
 
   NS = [
@@ -24,15 +24,13 @@ with dns.lib.combinators; {
   subdomains = rec {
     "*".A = [globals.routerPublicIPv4];
 
-    ns1 = {
+    ns = {
       A = [globals.routerPublicIPv4];
       AAAA = [];
     };
 
-    ns2 = {
-      A = ["192.145.57.90"];
-      AAAA = [];
-    };
+    ns1 = ns;
+    ns2 = ns;
 
     wg = {
       A = [globals.routerPublicIPv4];
diff --git a/modules/bootstrap-default.nix b/modules/bootstrap-default.nix
index dcacb10..edc12e8 100644
--- a/modules/bootstrap-default.nix
+++ b/modules/bootstrap-default.nix
@@ -15,9 +15,33 @@
           chart = nixhelm.chartsDerivations.${system}.metallb.metallb;
           includeCRDs = true;
         };
+
+        # argo-workflows = {
+        #   chart = nixhelm.chartsDerivations.${system}.argoproj.argo-workflows;
+        #   includeCRDs = true;
+        # };
+
+        longhorn = {
+          chart = nixhelm.chartsDerivations.${system}.longhorn.longhorn;
+          includeCRDs = true;
+
+          values = {
+            persistence.defaultClassReplicaCount = 2;
+            service.ui.type = "LoadBalancer";
+
+            defaultSettings = {
+              defaultDataPath = "/mnt/longhorn";
+              storageMinimalAvailablePercentage = 0;
+              allowRecurringJobWhileVolumeDetached = true;
+              backupTarget = "nfs://lewis.dmz:/mnt/longhorn/persistent/longhorn-backup";
+            };
+          };
+        };
       };
 
       resources = {
+        services.longhorn-frontend.spec.loadBalancerIP = globals.longhornIPv4;
+
         namespaces = {
           static-websites = {};
           freshrss = {};
@@ -35,21 +59,93 @@
           inbucket = {};
           dns = {};
           media = {};
+          minecraft = {};
           tailscale = {};
           ntfy = {};
-          authentik = {};
-          mealie = {};
         };
 
         nodes =
           builtins.mapAttrs
-          (_name: labels: {
+          (name: labels: {
             metadata.labels = labels;
           })
           globals.nodeLabels;
 
+        recurringJobs.backup-nfs.spec = {
+          cron = "0 1 * * *"; # One o'clock at night
+          task = "backup";
+          retain = 2; # We don't need many, as we also make Borg backups.
+          concurrency = 1;
+        };
+
         ipAddressPools.main.spec.addresses = ["192.168.30.128-192.168.30.200" "2a0d:6e00:1a77:30::2-2a0d:6e00:1a77:30:ffff:ffff:ffff:fffe"];
         l2Advertisements.main.metadata = {};
+
+        persistentVolumes = {
+          music-syncthing.spec = {
+            capacity.storage = "1Gi";
+            accessModes = ["ReadWriteMany"];
+
+            nfs = {
+              server = "lewis.dmz";
+              path = "/mnt/longhorn/persistent/media/music";
+            };
+          };
+
+          media-media.spec = {
+            capacity.storage = "1Gi";
+            accessModes = ["ReadWriteMany"];
+
+            nfs = {
+              server = "lewis.dmz";
+              path = "/mnt/longhorn/persistent/media";
+            };
+          };
+        };
+      };
+    };
+
+    lab = {
+      longhorn.persistentVolume = {
+        freshrss.storage = "1Gi";
+        radicale.storage = "200Mi";
+        atuin.storage = "300Mi";
+        atuin-db.storage = "300Mi";
+        nextcloud.storage = "50Gi";
+        nextcloud-db.storage = "400Mi";
+        hedgedoc-uploads.storage = "50Mi";
+        hedgedoc-db.storage = "100Mi";
+        kitchenowl.storage = "100Mi";
+        forgejo.storage = "20Gi";
+        paperless-data.storage = "10Gi";
+        paperless-redisdata.storage = "20Mi";
+        paperless-db.storage = "150Mi";
+        syncthing.storage = "400Mi";
+        pihole-data.storage = "750Mi";
+        pihole-dnsmasq.storage = "16Mi";
+        immich.storage = "50Gi";
+        immich-db.storage = "5Gi";
+        attic.storage = "15Gi";
+        attic-db.storage = "150Mi";
+        jellyfin.storage = "5Gi";
+        transmission.storage = "25Mi";
+        jellyseerr.storage = "75Mi";
+        radarr.storage = "300Mi";
+        prowlarr.storage = "150Mi";
+        sonarr.storage = "150Mi";
+        bazarr.storage = "25Mi";
+        minecraft.storage = "1Gi";
+        ntfy.storage = "300Mi";
+        deluge.storage = "500Mi";
+      };
+
+      tailscaleIngresses.tailscale-longhorn = {
+        host = "longhorn";
+
+        service = {
+          name = "longhorn-frontend";
+          portName = "http";
+        };
       };
     };
   };
diff --git a/modules/default.nix b/modules/default.nix
index 9409b69..c976c14 100644
--- a/modules/default.nix
+++ b/modules/default.nix
@@ -2,6 +2,7 @@
   imports = [
     ./inbucket.nix
     ./tailscale-ingress.nix
+    ./longhorn-volume.nix
     ./ingress.nix
     ./dummy-types.nix
     ./dnsmasq.nix
@@ -19,6 +20,7 @@
     ./forgejo
     ./paperless.nix
     ./syncthing.nix
+    ./pihole.nix
     ./immich.nix
     ./attic.nix
     ./bind9
@@ -26,7 +28,6 @@
     ./traefik.nix
     ./tailscale.nix
     ./ntfy.nix
-    ./authentik.nix
-    ./mealie.nix
+    ./minecraft.nix
   ];
 }
diff --git a/modules/dummy-types.nix b/modules/dummy-types.nix
index 14daf85..0a6a7f7 100644
--- a/modules/dummy-types.nix
+++ b/modules/dummy-types.nix
@@ -32,11 +32,11 @@
       kind = "ClusterIssuer";
     };
 
-    middlewares = {
-      attrName = "middlewares";
-      group = "traefik.io";
-      version = "v1alpha1";
-      kind = "Middleware";
+    recurringJob = {
+      attrName = "recurringJobs";
+      group = "longhorn.io";
+      version = "v1beta1";
+      kind = "RecurringJob";
     };
   };
 }
diff --git a/modules/forgejo/config.nix b/modules/forgejo/config.nix
index 133ad38..7f49f33 100644
--- a/modules/forgejo/config.nix
+++ b/modules/forgejo/config.nix
@@ -7,18 +7,11 @@
   "repository.pull-request".DEFAULT_MERGE_STYLE = "merge";
   "repository.signing".DEFAULT_TRUST_MODEL = "committer";
   ui.DEFAULT_THEME = "forgejo-light";
-
   oauth2 = {
-    ENABLED = true;
+    ENABLED = false;
     JWT_SECRET = "ref+sops://secrets.yml#/forgejo/jwtSecret";
   };
 
-  oauth2_client = {
-    ENABLE_AUTO_REGISTRATION = true;
-    ACCOUNT_LINKING = "auto";
-    USERNAME = "email";
-  };
-
   DEFAULT = {
     APP_NAME = "Forgejo: Beyond coding. We forge.";
     RUN_MODE = "prod";
@@ -92,11 +85,11 @@
   };
 
   service = {
-    DISABLE_REGISTRATION = false;
+    DISABLE_REGISTRATION = true;
     REQUIRE_SIGNIN_VIEW = false;
     REGISTER_EMAIL_CONFIRM = false;
     ENABLE_NOTIFY_MAIL = false;
-    ALLOW_ONLY_EXTERNAL_REGISTRATION = true;
+    ALLOW_ONLY_EXTERNAL_REGISTRATION = false;
     ENABLE_CAPTCHA = false;
     DEFAULT_KEEP_EMAIL_PRIVATE = true;
     DEFAULT_ALLOW_CREATE_ORGANIZATION = true;
@@ -105,7 +98,7 @@
   };
 
   openid = {
-    ENABLE_OPENID_SIGNIN = false;
+    ENABLE_OPENID_SIGNIN = true;
     ENABLE_OPENID_SIGNUP = false;
   };
 }
diff --git a/modules/forgejo/default.nix b/modules/forgejo/default.nix
index 0e3f3fd..c35a5fc 100644
--- a/modules/forgejo/default.nix
+++ b/modules/forgejo/default.nix
@@ -29,7 +29,6 @@
             # This disables services from becoming environmental variables
             # to prevent SSH_PORT clashing with Forgejo config.
             enableServiceLinks = false;
-            nodeName = "jefke";
 
             containers.forgejo = {
               image = globals.images.forgejo;
@@ -59,10 +58,7 @@
             };
 
             volumes = {
-              data.hostPath = {
-                path = "/mnt/longhorn/persistent/volumes/forgejo";
-                type = "Directory";
-              };
+              data.persistentVolumeClaim.claimName = "data";
               config.secret.secretName = "forgejo";
             };
           };
@@ -101,6 +97,11 @@
           portName = "web";
         };
       };
+
+      longhorn.persistentVolumeClaim.data = {
+        volumeName = "forgejo";
+        storage = "20Gi";
+      };
     };
   };
 }
diff --git a/modules/freshrss.nix b/modules/freshrss.nix
index 9b8c851..0581d95 100644
--- a/modules/freshrss.nix
+++ b/modules/freshrss.nix
@@ -26,8 +26,6 @@
           metadata.labels.app = "freshrss";
 
           spec = {
-            nodeName = "atlas";
-
             containers.freshrss = {
               image = globals.images.freshrss;
               imagePullPolicy = "IfNotPresent";
@@ -38,13 +36,6 @@
                 CRON_MIN.value = "2,32";
                 ADMIN_EMAIL.value = "pim@kunis.nl";
                 PUBLISHED_PORT.value = "443";
-                OIDC_ENABLED.value = "1";
-                OIDC_PROVIDER_METADATA_URL.value = "https://authentik.kun.is/application/o/freshrss/.well-known/openid-configuration";
-                OIDC_CLIENT_ID.value = "5J2L7Ufq4KMayQ8qrqxHCslxHWL2SXNMKJmsbbiQ";
-                OIDC_CLIENT_SECRET.value = "ref+sops://secrets.yml#/authentik/oauth2/freshrss/client_secret";
-                OIDC_CLIENT_CRYPTO_KEY.value = "ref+sops://secrets.yml#/freshrss/oidc_crypto_key";
-                OIDC_SCOPES.value = "openid email profile";
-                OIDC_X_FORWARDED_HEADERS.value = "X-Forwarded-Port X-Forwarded-Proto X-Forwarded-Host";
 
                 ADMIN_PASSWORD.valueFrom.secretKeyRef = {
                   name = "server";
@@ -65,10 +56,7 @@
               ];
             };
 
-            volumes.data.hostPath = {
-              path = "/mnt/longhorn/persistent/volumes/freshrss";
-              type = "Directory";
-            };
+            volumes.data.persistentVolumeClaim.claimName = "data";
 
             securityContext = {
               fsGroup = 33;
@@ -95,6 +83,11 @@
         host = "freshrss";
         service.name = "server";
       };
+
+      longhorn.persistentVolumeClaim.data = {
+        volumeName = "freshrss";
+        storage = "1Gi";
+      };
     };
   };
 }
diff --git a/modules/hedgedoc.nix b/modules/hedgedoc.nix
index 9fa0513..d41b200 100644
--- a/modules/hedgedoc.nix
+++ b/modules/hedgedoc.nix
@@ -41,8 +41,6 @@
             };
 
             spec = {
-              nodeName = "jefke";
-
               containers.hedgedoc = {
                 image = globals.images.hedgedoc;
                 ports.web.containerPort = 3000;
@@ -56,17 +54,6 @@
                   CMD_PROTOCOL_USESSL.value = "true";
                   CMD_CSP_ENABLE.value = "false";
 
-                  CMD_OAUTH2_PROVIDERNAME.value = "Authentik";
-                  CMD_OAUTH2_CLIENT_ID.value = "ZF56062l4BPnq2INv2zaO9cEiE6sAj7CrxbWhExj";
-                  CMD_OAUTH2_CLIENT_SECRET.value = "ref+sops://secrets.yml#/authentik/oauth2/hedgedoc/client_secret";
-                  CMD_OAUTH2_SCOPE.value = "openid email profile";
-                  CMD_OAUTH2_USER_PROFILE_URL.value = "https://authentik.kun.is/application/o/userinfo/";
-                  CMD_OAUTH2_TOKEN_URL.value = "https://authentik.kun.is/application/o/token/";
-                  CMD_OAUTH2_AUTHORIZATION_URL.value = "https://authentik.kun.is/application/o/authorize/";
-                  CMD_OAUTH2_USER_PROFILE_USERNAME_ATTR.value = "preferred_username";
-                  CMD_OAUTH2_USER_PROFILE_DISPLAY_NAME_ATTR.value = "name";
-                  CMD_OAUTH2_USER_PROFILE_EMAIL_ATTR.value = "email";
-
                   CMD_DB_URL.valueFrom.secretKeyRef = {
                     name = "hedgedoc";
                     key = "databaseURL";
@@ -92,12 +79,8 @@
               };
 
               volumes = {
+                uploads.persistentVolumeClaim.claimName = "uploads";
                 config.configMap.name = "hedgedoc-config";
-
-                uploads.hostPath = {
-                  path = "/mnt/longhorn/persistent/volumes/hedgedoc-uploads";
-                  type = "Directory";
-                };
               };
 
               securityContext = {
@@ -130,8 +113,6 @@
             };
 
             spec = {
-              nodeName = "jefke";
-
               containers.postgres = {
                 image = globals.images.postgres15;
                 imagePullPolicy = "IfNotPresent";
@@ -156,10 +137,7 @@
                 ];
               };
 
-              volumes.database.hostPath = {
-                path = "/mnt/longhorn/persistent/volumes/hedgedoc-db";
-                type = "Directory";
-              };
+              volumes.database.persistentVolumeClaim.claimName = "database";
             };
           };
         };
@@ -201,6 +179,18 @@
           portName = "web";
         };
       };
+
+      longhorn.persistentVolumeClaim = {
+        uploads = {
+          volumeName = "hedgedoc-uploads";
+          storage = "50Mi";
+        };
+
+        database = {
+          volumeName = "hedgedoc-db";
+          storage = "100Mi";
+        };
+      };
     };
   };
 }
diff --git a/modules/immich.nix b/modules/immich.nix
index 81700d4..0317b8b 100644
--- a/modules/immich.nix
+++ b/modules/immich.nix
@@ -33,12 +33,7 @@
             };
 
             spec = {
-              nodeName = "jefke";
-
-              volumes.data.hostPath = {
-                path = "/mnt/longhorn/persistent/volumes/immich";
-                type = "Directory";
-              };
+              volumes.data.persistentVolumeClaim.claimName = "data";
 
               enableServiceLinks = false;
 
@@ -94,10 +89,7 @@
             };
 
             spec = {
-              volumes.cache.hostPath = {
-                path = "/tmp/immich-ml-cache";
-                type = "DirectoryOrCreate";
-              };
+              volumes.cache.persistentVolumeClaim.claimName = "cache";
 
               containers.machine-learning = {
                 image = globals.images.immich-machine-learning;
@@ -169,16 +161,13 @@
             };
 
             spec = {
-              nodeName = "jefke";
-
-              volumes.data.hostPath = {
-                path = "/mnt/longhorn/persistent/volumes/immich-db";
-                type = "Directory";
-              };
+              volumes.data.persistentVolumeClaim.claimName = "database";
 
               containers.postgres = {
                 image = globals.images.immich-postgres;
                 imagePullPolicy = "IfNotPresent";
+                command = ["postgres"];
+                args = ["-c" "shared_preload_libraries=vectors.so" "-c" "search_path=\"$$user\", public, vectors" "-c" "logging_collector=on" "-c" "max_wal_size=2GB" "-c" "shared_buffers=512MB" "-c" "wal_compression=on"];
                 ports.postgres.containerPort = 5432;
                 securityContext.runAsUser = 999;
                 securityContext.runAsGroup = 999;
@@ -259,6 +248,11 @@
           };
         };
       };
+
+      persistentVolumeClaims.cache.spec = {
+        accessModes = ["ReadWriteOnce"];
+        resources.requests.storage = "5Gi";
+      };
     };
 
     lab = {
@@ -266,6 +260,18 @@
         host = "immich";
         service.name = "server";
       };
+
+      longhorn.persistentVolumeClaim = {
+        data = {
+          volumeName = "immich";
+          storage = "50Gi";
+        };
+
+        database = {
+          volumeName = "immich-db";
+          storage = "5Gi";
+        };
+      };
     };
   };
 }
diff --git a/modules/inbucket.nix b/modules/inbucket.nix
index 7da77a7..7fbc481 100644
--- a/modules/inbucket.nix
+++ b/modules/inbucket.nix
@@ -21,14 +21,7 @@
 
             containers.inbucket = {
               image = globals.images.inbucket;
-              env = {
-                INBUCKET_MAILBOXNAMING.value = "full";
-                INBUCKET_SMTP_DEFAULTACCEPT.value = "false";
-                INBUCKET_SMTP_ACCEPTDOMAINS.value = "kun.is";
-                INBUCKET_SMTP_DEFAULTSTORE.value = "false";
-                INBUCKET_SMTP_STOREDOMAINS.value = "kun.is";
-                INBUCKET_STORAGE_RETENTIONPERIOD.value = "168h";
-              };
+
               ports = {
                 web.containerPort = 9000;
                 smtp.containerPort = 2500;
diff --git a/modules/ingress.nix b/modules/ingress.nix
index d6a27aa..9dbcb86 100644
--- a/modules/ingress.nix
+++ b/modules/ingress.nix
@@ -47,7 +47,7 @@ in {
 
           rules = [
             {
-              inherit (ingress) host;
+              host = ingress.host;
 
               http.paths = [
                 {
@@ -55,7 +55,7 @@ in {
                   pathType = "Prefix";
 
                   backend.service = {
-                    inherit (ingress.service) name;
+                    name = ingress.service.name;
                     port.name = ingress.service.portName;
                   };
                 }
diff --git a/modules/kitchenowl.nix b/modules/kitchenowl.nix
index 9e01de4..1042468 100644
--- a/modules/kitchenowl.nix
+++ b/modules/kitchenowl.nix
@@ -26,28 +26,16 @@
           metadata.labels.app = "kitchenowl";
 
           spec = {
-            nodeName = "jefke";
-
-            volumes.data.hostPath = {
-              path = "/mnt/longhorn/persistent/volumes/kitchenowl";
-              type = "Directory";
-            };
+            volumes.data.persistentVolumeClaim.claimName = "data";
 
             containers.kitchenowl = {
               image = globals.images.kitchenowl;
               ports.web.containerPort = 8080;
               imagePullPolicy = "IfNotPresent";
 
-              env = {
-                FRONT_URL.value = "https://boodschappen.kun.is";
-                OIDC_ISSUER.value = "https://authentik.kun.is/application/o/kitchenowl/";
-                OIDC_CLIENT_ID.value = "OptR5S9hPix9beuJWFdfNBWRBr2l0nPx7mj8FpB3";
-                OIDC_CLIENT_SECRET.value = "ref+sops://secrets.yml#/authentik/oauth2/kitchenowl/client_secret";
-
-                JWT_SECRET_KEY.valueFrom.secretKeyRef = {
-                  name = "server";
-                  key = "jwtSecretKey";
-                };
+              env.JWT_SECRET_KEY.valueFrom.secretKeyRef = {
+                name = "server";
+                key = "jwtSecretKey";
               };
 
               volumeMounts = [
@@ -85,6 +73,11 @@
           portName = "web";
         };
       };
+
+      longhorn.persistentVolumeClaim.data = {
+        volumeName = "kitchenowl";
+        storage = "100Mi";
+      };
     };
   };
 }
diff --git a/modules/longhorn-volume.nix b/modules/longhorn-volume.nix
new file mode 100644
index 0000000..6eb4bfd
--- /dev/null
+++ b/modules/longhorn-volume.nix
@@ -0,0 +1,157 @@
+{
+  lib,
+  config,
+  ...
+}: let
+  longhornVolumeOpts = {name, ...}: {
+    options = {
+      storage = lib.mkOption {
+        type = lib.types.str;
+      };
+
+      namespace = lib.mkOption {
+        type = lib.types.str;
+        default = "default";
+      };
+    };
+  };
+
+  longhornPVOpts = {name, ...}: {
+    options = {
+      storage = lib.mkOption {
+        type = lib.types.str;
+      };
+    };
+  };
+
+  longhornPVCOpts = {name, ...}: {
+    options = {
+      volumeName = lib.mkOption {
+        type = lib.types.str;
+        default = name;
+      };
+
+      # TODO: ideally we take this from the longhornPV so we don't duplicate this information.
+      storage = lib.mkOption {
+        type = lib.types.str;
+      };
+    };
+  };
+in {
+  options = {
+    lab.longhornVolumes = lib.mkOption {
+      type = with lib.types; attrsOf (submodule longhornVolumeOpts);
+      default = {};
+    };
+
+    lab.longhorn = {
+      persistentVolume = lib.mkOption {
+        type = with lib.types; attrsOf (submodule longhornPVOpts);
+        default = {};
+      };
+
+      persistentVolumeClaim = lib.mkOption {
+        type = with lib.types; attrsOf (submodule longhornPVCOpts);
+        default = {};
+      };
+    };
+  };
+
+  config = {
+    kubernetes.resources = {
+      persistentVolumes =
+        lib.mergeAttrs
+        (builtins.mapAttrs
+          (name: longhornVolume: {
+            spec = {
+              accessModes = ["ReadWriteOnce"];
+              capacity.storage = longhornVolume.storage;
+              persistentVolumeReclaimPolicy = "Delete";
+              volumeMode = "Filesystem";
+
+              claimRef = {
+                inherit name;
+                namespace = longhornVolume.namespace;
+              };
+
+              csi = {
+                driver = "driver.longhorn.io";
+                fsType = "ext4";
+                volumeHandle = name;
+
+                volumeAttributes = {
+                  dataLocality = "disabled";
+                  fromBackup = "";
+                  fsType = "ext4";
+                  numberOfReplicas = "2";
+                  staleReplicaTimeout = "30";
+                  unmapMarkSnapChainRemoved = "ignored";
+
+                  recurringJobSelector = lib.generators.toYAML {} [
+                    {
+                      name = "backup-nfs";
+                      isGroup = false;
+                    }
+                  ];
+                };
+              };
+            };
+          })
+          config.lab.longhornVolumes)
+        (builtins.mapAttrs
+          (name: longhornPV: {
+            spec = {
+              accessModes = ["ReadWriteOnce"];
+              capacity.storage = longhornPV.storage;
+              persistentVolumeReclaimPolicy = "Delete";
+              volumeMode = "Filesystem";
+
+              csi = {
+                driver = "driver.longhorn.io";
+                fsType = "ext4";
+                volumeHandle = name;
+
+                volumeAttributes = {
+                  dataLocality = "disabled";
+                  fromBackup = "";
+                  fsType = "ext4";
+                  numberOfReplicas = "2";
+                  staleReplicaTimeout = "30";
+                  unmapMarkSnapChainRemoved = "ignored";
+
+                  recurringJobSelector = lib.generators.toYAML {} [
+                    {
+                      name = "backup-nfs";
+                      isGroup = false;
+                    }
+                  ];
+                };
+              };
+            };
+          })
+          config.lab.longhorn.persistentVolume);
+
+      persistentVolumeClaims =
+        lib.mergeAttrs
+        (builtins.mapAttrs
+          (name: longhornVolume: {
+            spec = {
+              accessModes = ["ReadWriteOnce"];
+              resources.requests.storage = longhornVolume.storage;
+              storageClassName = "";
+            };
+          })
+          config.lab.longhornVolumes)
+        (builtins.mapAttrs
+          (name: longhornPVC: {
+            spec = {
+              accessModes = ["ReadWriteOnce"];
+              resources.requests.storage = longhornPVC.storage;
+              storageClassName = "";
+              volumeName = longhornPVC.volumeName;
+            };
+          })
+          config.lab.longhorn.persistentVolumeClaim);
+    };
+  };
+}
diff --git a/modules/mealie.nix b/modules/mealie.nix
deleted file mode 100644
index 8e559f6..0000000
--- a/modules/mealie.nix
+++ /dev/null
@@ -1,76 +0,0 @@
-{
-  lib,
-  config,
-  utils,
-  ...
-}: {
-  options.mealie.enable = lib.mkEnableOption "mealie";
-
-  config = lib.mkIf config.mealie.enable {
-    kubernetes.resources = {
-      deployments.mealie.spec = {
-        selector.matchLabels.app = "mealie";
-
-        strategy = {
-          type = "RollingUpdate";
-
-          rollingUpdate = {
-            maxSurge = 0;
-            maxUnavailable = 1;
-          };
-        };
-
-        template = {
-          metadata.labels.app = "mealie";
-
-          spec = {
-            nodeName = "atlas";
-
-            containers.mealie = {
-              image = utils.mkNixNGImage "mealie";
-              ports.web.containerPort = 8000;
-
-              env = {
-                SMTP_USER.value = "ref+sops://secrets.yml#/smtp2go/username";
-                SMTP_PASSWORD.value = "ref+sops://secrets.yml#/smtp2go/password";
-                OIDC_CLIENT_SECRET.value = "ref+sops://secrets.yml#/authentik/oauth2/mealie/client_secret";
-              };
-
-              volumeMounts = [
-                {
-                  name = "mealie";
-                  mountPath = "/data";
-                }
-              ];
-            };
-
-            volumes.mealie.hostPath = {
-              path = "/mnt/longhorn/persistent/volumes/mealie";
-              type = "Directory";
-            };
-          };
-        };
-      };
-
-      services.mealie.spec = {
-        selector.app = "mealie";
-
-        ports.web = {
-          port = 80;
-          targetPort = "web";
-        };
-      };
-    };
-
-    lab = {
-      ingresses.mealie = {
-        host = "mealie.kun.is";
-
-        service = {
-          name = "mealie";
-          portName = "web";
-        };
-      };
-    };
-  };
-}
diff --git a/modules/media.nix b/modules/media.nix
index 248991c..3b4be3e 100644
--- a/modules/media.nix
+++ b/modules/media.nix
@@ -4,462 +4,415 @@
   lib,
   utils,
   ...
-}: let
-  cfg = config.media;
-in {
-  options.media = {
-    enable = lib.mkEnableOption "media";
-    jellyfin.enable = (lib.mkEnableOption "jellyfin") // {default = true;};
-    deluge.enable = (lib.mkEnableOption "deluge") // {default = true;};
-    jellyseerr.enable = (lib.mkEnableOption "jellyseerr") // {default = true;};
-    radarr.enable = (lib.mkEnableOption "radarr") // {default = true;};
-    prowlarr.enable = (lib.mkEnableOption "prowlarr") // {default = true;};
-    sonarr.enable = (lib.mkEnableOption "sonarr") // {default = true;};
-    bazarr.enable = (lib.mkEnableOption "bazarr") // {default = true;};
-  };
+}: {
+  options.media.enable = lib.mkEnableOption "media";
 
-  config = lib.mkIf cfg.enable {
+  config = lib.mkIf config.media.enable {
     kubernetes.resources = {
       deployments = {
-        jellyfin = lib.mkIf cfg.jellyfin.enable {
-          spec = {
-            selector.matchLabels = {
+        jellyfin.spec = {
+          selector.matchLabels = {
+            app = "media";
+            component = "jellyfin";
+          };
+
+          strategy = {
+            type = "RollingUpdate";
+
+            rollingUpdate = {
+              maxSurge = 0;
+              maxUnavailable = 1;
+            };
+          };
+
+          template = {
+            metadata.labels = {
               app = "media";
               component = "jellyfin";
             };
 
-            strategy = {
-              type = "RollingUpdate";
+            spec = {
+              containers.jellyfin = {
+                image = globals.images.jellyfin;
+                ports.web.containerPort = 8096;
+                imagePullPolicy = "IfNotPresent";
 
-              rollingUpdate = {
-                maxSurge = 0;
-                maxUnavailable = 1;
-              };
-            };
+                env.JELLYFIN_PublishedServerUrl.value = "https://media.kun.is";
 
-            template = {
-              metadata.labels = {
-                app = "media";
-                component = "jellyfin";
+                volumeMounts = [
+                  {
+                    name = "config";
+                    mountPath = "/config";
+                  }
+                  {
+                    name = "media";
+                    mountPath = "/media";
+                  }
+                  {
+                    name = "cache";
+                    mountPath = "/config/transcodes";
+                  }
+                ];
               };
 
-              spec = {
-                nodeName = "lewis";
+              volumes = {
+                config.persistentVolumeClaim.claimName = "jellyfin";
+                cache.persistentVolumeClaim.claimName = "jellyfin-cache";
 
-                containers.jellyfin = {
-                  image = globals.images.jellyfin;
-                  ports.web.containerPort = 8096;
-                  imagePullPolicy = "IfNotPresent";
+                media.hostPath = {
+                  path = "/mnt/longhorn/persistent/media";
+                  type = "Directory";
+                };
+              };
 
-                  env.JELLYFIN_PublishedServerUrl.value = "https://media.kun.is";
+              securityContext = {
+                fsGroup = 0;
+                fsGroupChangePolicy = "OnRootMismatch";
+              };
 
-                  volumeMounts = [
+              affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms = [
+                {
+                  matchExpressions = [
                     {
-                      name = "config";
-                      mountPath = "/config";
-                    }
-                    {
-                      name = "media";
-                      mountPath = "/media";
-                    }
-                    {
-                      name = "cache";
-                      mountPath = "/config/transcodes";
-                    }
-                    {
-                      name = "music";
-                      mountPath = "/media/music";
+                      key = "hasMedia";
+                      operator = "In";
+                      values = ["true"];
                     }
                   ];
-                };
-
-                volumes = {
-                  cache.hostPath = {
-                    path = "/tmp/jellyfin-cache";
-                    type = "DirectoryOrCreate";
-                  };
-
-                  music.hostPath = {
-                    path = "/mnt/longhorn/persistent/music";
-                    type = "Directory";
-                  };
-
-                  config.hostPath = {
-                    path = "/mnt/longhorn/persistent/volumes/jellyfin";
-                    type = "Directory";
-                  };
-
-                  media.hostPath = {
-                    path = "/mnt/longhorn/persistent/media";
-                    type = "Directory";
-                  };
-                };
-              };
+                }
+              ];
             };
           };
         };
 
-        deluge = lib.mkIf cfg.deluge.enable {
-          spec = {
-            selector.matchLabels = {
+        deluge.spec = {
+          selector.matchLabels = {
+            app = "media";
+            component = "deluge";
+          };
+
+          strategy = {
+            type = "RollingUpdate";
+
+            rollingUpdate = {
+              maxSurge = 0;
+              maxUnavailable = 1;
+            };
+          };
+
+          template = {
+            metadata.labels = {
               app = "media";
               component = "deluge";
             };
 
-            strategy = {
-              type = "RollingUpdate";
+            spec = {
+              containers.deluge = {
+                image = globals.images.deluge;
+                imagePullPolicy = "IfNotPresent";
 
-              rollingUpdate = {
-                maxSurge = 0;
-                maxUnavailable = 1;
-              };
-            };
-
-            template = {
-              metadata.labels = {
-                app = "media";
-                component = "deluge";
-              };
-
-              spec = {
-                nodeName = "lewis";
-
-                containers.deluge = {
-                  image = utils.mkNixNGImage "deluge";
-                  imagePullPolicy = "IfNotPresent";
-
-                  env = {
-                    PUID.value = "1000";
-                    PGID.value = "1000";
-                    TZ.value = "Europe/Amsterdam";
-                    DELUGE_LOGLEVEL.value = "info";
-                  };
-
-                  ports = {
-                    web.containerPort = 8112;
-                    bittorrent.containerPort = 31780;
-                  };
-
-                  volumeMounts = [
-                    {
-                      name = "config";
-                      mountPath = "/config";
-                    }
-                    {
-                      name = "media";
-                      mountPath = "/media";
-                    }
-                  ];
+                env = {
+                  PUID.value = "1000";
+                  PGID.value = "1000";
+                  TZ.value = "Europe/Amsterdam";
+                  DELUGE_LOGLEVEL.value = "info";
                 };
 
-                volumes = {
-                  config.hostPath = {
-                    path = "/mnt/longhorn/persistent/volumes/deluge";
-                    type = "Directory";
-                  };
-
-                  media.hostPath = {
-                    path = "/mnt/longhorn/persistent/media";
-                    type = "Directory";
-                  };
+                ports = {
+                  web.containerPort = 8112;
+                  bittorrent.containerPort = 31780;
                 };
+
+                volumeMounts = [
+                  {
+                    name = "config";
+                    mountPath = "/config";
+                  }
+                  {
+                    name = "media";
+                    mountPath = "/media";
+                  }
+                ];
+              };
+
+              volumes = {
+                config.persistentVolumeClaim.claimName = "deluge";
+                media.persistentVolumeClaim.claimName = "media";
               };
             };
           };
         };
 
-        jellyseerr = lib.mkIf cfg.jellyseerr.enable {
-          spec = {
-            selector.matchLabels = {
+        jellyseerr.spec = {
+          selector.matchLabels = {
+            app = "media";
+            component = "jellyseerr";
+          };
+
+          strategy = {
+            type = "RollingUpdate";
+
+            rollingUpdate = {
+              maxSurge = 0;
+              maxUnavailable = 1;
+            };
+          };
+
+          template = {
+            metadata.labels = {
               app = "media";
               component = "jellyseerr";
             };
 
-            strategy = {
-              type = "RollingUpdate";
+            spec = {
+              volumes.config.persistentVolumeClaim.claimName = "jellyseerr";
 
-              rollingUpdate = {
-                maxSurge = 0;
-                maxUnavailable = 1;
-              };
-            };
+              containers.jellyseerr = {
+                image = utils.mkNixNGImage "jellyseerr";
+                ports.web.containerPort = 5055;
+                imagePullPolicy = "IfNotPresent";
 
-            template = {
-              metadata.labels = {
-                app = "media";
-                component = "jellyseerr";
-              };
-
-              spec = {
-                nodeName = "lewis";
-
-                volumes.config.hostPath = {
-                  path = "/mnt/longhorn/persistent/volumes/jellyseerr";
-                  type = "Directory";
+                env = {
+                  LOG_LEVEL.value = "debug";
+                  TZ.value = "Europe/Amsterdam";
                 };
 
-                containers.jellyseerr = {
-                  image = utils.mkNixNGImage "jellyseerr";
-                  ports.web.containerPort = 5055;
-                  imagePullPolicy = "IfNotPresent";
+                volumeMounts = [
+                  {
+                    name = "config";
+                    mountPath = "/app/config";
+                  }
+                ];
+              };
 
-                  env = {
-                    LOG_LEVEL.value = "debug";
-                    TZ.value = "Europe/Amsterdam";
-                  };
-
-                  volumeMounts = [
-                    {
-                      name = "config";
-                      mountPath = "/app/config";
-                    }
-                  ];
-                };
+              securityContext = {
+                fsGroup = 0;
+                fsGroupChangePolicy = "OnRootMismatch";
               };
             };
           };
         };
 
-        radarr = lib.mkIf cfg.radarr.enable {
-          spec = {
-            selector.matchLabels = {
+        radarr.spec = {
+          selector.matchLabels = {
+            app = "media";
+            component = "radarr";
+          };
+
+          strategy = {
+            type = "RollingUpdate";
+
+            rollingUpdate = {
+              maxSurge = 0;
+              maxUnavailable = 1;
+            };
+          };
+
+          template = {
+            metadata.labels = {
               app = "media";
               component = "radarr";
             };
 
-            strategy = {
-              type = "RollingUpdate";
+            spec = {
+              containers.radarr = {
+                image = utils.mkNixNGImage "radarr";
+                ports.web.containerPort = 7878;
+                imagePullPolicy = "IfNotPresent";
 
-              rollingUpdate = {
-                maxSurge = 0;
-                maxUnavailable = 1;
-              };
-            };
-
-            template = {
-              metadata.labels = {
-                app = "media";
-                component = "radarr";
-              };
-
-              spec = {
-                nodeName = "lewis";
-
-                containers.radarr = {
-                  image = utils.mkNixNGImage "radarr";
-                  ports.web.containerPort = 7878;
-                  imagePullPolicy = "IfNotPresent";
-
-                  env = {
-                    PUID.value = "1000";
-                    PGID.value = "1000";
-                    TZ.value = "Europe/Amsterdam";
-                  };
-
-                  volumeMounts = [
-                    {
-                      name = "config";
-                      mountPath = "/config";
-                    }
-                    {
-                      name = "media";
-                      mountPath = "/media";
-                    }
-                  ];
+                env = {
+                  PUID.value = "1000";
+                  PGID.value = "1000";
+                  TZ.value = "Europe/Amsterdam";
                 };
 
-                volumes = {
-                  config.hostPath = {
-                    path = "/mnt/longhorn/persistent/volumes/radarr";
-                    type = "Directory";
-                  };
+                volumeMounts = [
+                  {
+                    name = "config";
+                    mountPath = "/config";
+                  }
+                  {
+                    name = "media";
+                    mountPath = "/media";
+                  }
+                ];
+              };
 
-                  media.hostPath = {
-                    path = "/mnt/longhorn/persistent/media";
-                    type = "Directory";
-                  };
-                };
+              volumes = {
+                config.persistentVolumeClaim.claimName = "radarr";
+                media.persistentVolumeClaim.claimName = "media";
+              };
+
+              securityContext = {
+                fsGroup = 1000;
+                fsGroupChangePolicy = "OnRootMismatch";
               };
             };
           };
         };
 
-        prowlarr = lib.mkIf cfg.prowlarr.enable {
-          spec = {
-            selector.matchLabels = {
+        prowlarr.spec = {
+          selector.matchLabels = {
+            app = "media";
+            component = "prowlarr";
+          };
+
+          strategy = {
+            type = "RollingUpdate";
+
+            rollingUpdate = {
+              maxSurge = 0;
+              maxUnavailable = 1;
+            };
+          };
+
+          template = {
+            metadata.labels = {
               app = "media";
               component = "prowlarr";
             };
 
-            strategy = {
-              type = "RollingUpdate";
+            spec = {
+              volumes.config.persistentVolumeClaim.claimName = "prowlarr";
 
-              rollingUpdate = {
-                maxSurge = 0;
-                maxUnavailable = 1;
-              };
-            };
+              containers.prowlarr = {
+                image = utils.mkNixNGImage "prowlarr";
+                ports.web.containerPort = 9696;
+                imagePullPolicy = "IfNotPresent";
 
-            template = {
-              metadata.labels = {
-                app = "media";
-                component = "prowlarr";
-              };
-
-              spec = {
-                nodeName = "lewis";
-
-                volumes.config.hostPath = {
-                  path = "/mnt/longhorn/persistent/volumes/prowlarr";
-                  type = "Directory";
+                env = {
+                  PUID.value = "1000";
+                  PGID.value = "1000";
+                  TZ.value = "Europe/Amsterdam";
                 };
 
-                containers.prowlarr = {
-                  image = utils.mkNixNGImage "prowlarr";
-                  ports.web.containerPort = 9696;
-                  imagePullPolicy = "IfNotPresent";
+                volumeMounts = [
+                  {
+                    name = "config";
+                    mountPath = "/config";
+                  }
+                ];
+              };
 
-                  env = {
-                    PUID.value = "1000";
-                    PGID.value = "1000";
-                    TZ.value = "Europe/Amsterdam";
-                  };
-
-                  volumeMounts = [
-                    {
-                      name = "config";
-                      mountPath = "/config";
-                    }
-                  ];
-                };
+              securityContext = {
+                fsGroup = 1000;
+                fsGroupChangePolicy = "OnRootMismatch";
               };
             };
           };
         };
 
-        sonarr = lib.mkIf cfg.sonarr.enable {
-          spec = {
-            selector.matchLabels = {
+        sonarr.spec = {
+          selector.matchLabels = {
+            app = "media";
+            component = "sonarr";
+          };
+
+          strategy = {
+            type = "RollingUpdate";
+
+            rollingUpdate = {
+              maxSurge = 0;
+              maxUnavailable = 1;
+            };
+          };
+
+          template = {
+            metadata.labels = {
               app = "media";
               component = "sonarr";
             };
 
-            strategy = {
-              type = "RollingUpdate";
+            spec = {
+              containers.sonarr = {
+                image = utils.mkNixNGImage "sonarr";
+                ports.web.containerPort = 8989;
+                imagePullPolicy = "IfNotPresent";
 
-              rollingUpdate = {
-                maxSurge = 0;
-                maxUnavailable = 1;
-              };
-            };
-
-            template = {
-              metadata.labels = {
-                app = "media";
-                component = "sonarr";
-              };
-
-              spec = {
-                nodeName = "lewis";
-
-                containers.sonarr = {
-                  image = utils.mkNixNGImage "sonarr";
-                  ports.web.containerPort = 8989;
-                  imagePullPolicy = "IfNotPresent";
-
-                  env = {
-                    PUID.value = "1000";
-                    PGID.value = "1000";
-                    TZ.value = "Europe/Amsterdam";
-                  };
-
-                  volumeMounts = [
-                    {
-                      name = "config";
-                      mountPath = "/config";
-                    }
-                    {
-                      name = "media";
-                      mountPath = "/media";
-                    }
-                  ];
+                env = {
+                  PUID.value = "1000";
+                  PGID.value = "1000";
+                  TZ.value = "Europe/Amsterdam";
                 };
 
-                volumes = {
-                  config.hostPath = {
-                    path = "/mnt/longhorn/persistent/volumes/sonarr";
-                    type = "Directory";
-                  };
+                volumeMounts = [
+                  {
+                    name = "config";
+                    mountPath = "/config";
+                  }
+                  {
+                    name = "media";
+                    mountPath = "/media";
+                  }
+                ];
+              };
 
-                  media.hostPath = {
-                    path = "/mnt/longhorn/persistent/media";
-                    type = "Directory";
-                  };
-                };
+              volumes = {
+                config.persistentVolumeClaim.claimName = "sonarr";
+                media.persistentVolumeClaim.claimName = "media";
+              };
+
+              securityContext = {
+                fsGroup = 1000;
+                fsGroupChangePolicy = "OnRootMismatch";
               };
             };
           };
         };
 
-        bazarr = lib.mkIf cfg.bazarr.enable {
-          spec = {
-            selector.matchLabels = {
+        bazarr.spec = {
+          selector.matchLabels = {
+            app = "media";
+            component = "bazarr";
+          };
+
+          strategy = {
+            type = "RollingUpdate";
+
+            rollingUpdate = {
+              maxSurge = 0;
+              maxUnavailable = 1;
+            };
+          };
+
+          template = {
+            metadata.labels = {
               app = "media";
               component = "bazarr";
             };
 
-            strategy = {
-              type = "RollingUpdate";
+            spec = {
+              containers.bazarr = {
+                image = utils.mkNixNGImage "bazarr";
+                ports.web.containerPort = 6767;
+                imagePullPolicy = "IfNotPresent";
 
-              rollingUpdate = {
-                maxSurge = 0;
-                maxUnavailable = 1;
-              };
-            };
-
-            template = {
-              metadata.labels = {
-                app = "media";
-                component = "bazarr";
-              };
-
-              spec = {
-                nodeName = "lewis";
-
-                containers.bazarr = {
-                  image = utils.mkNixNGImage "bazarr";
-                  ports.web.containerPort = 6767;
-                  imagePullPolicy = "IfNotPresent";
-
-                  env = {
-                    PUID.value = "1000";
-                    PGID.value = "1000";
-                    TZ.value = "Europe/Amsterdam";
-                  };
-
-                  volumeMounts = [
-                    {
-                      name = "config";
-                      mountPath = "/config";
-                    }
-                    {
-                      name = "media";
-                      mountPath = "/media";
-                    }
-                  ];
+                env = {
+                  PUID.value = "1000";
+                  PGID.value = "1000";
+                  TZ.value = "Europe/Amsterdam";
                 };
 
-                volumes = {
-                  config.hostPath = {
-                    path = "/mnt/longhorn/persistent/volumes/bazarr";
-                    type = "Directory";
-                  };
+                volumeMounts = [
+                  {
+                    name = "config";
+                    mountPath = "/config";
+                  }
+                  {
+                    name = "media";
+                    mountPath = "/media";
+                  }
+                ];
+              };
 
-                  media.hostPath = {
-                    path = "/mnt/longhorn/persistent/media";
-                    type = "Directory";
-                  };
-                };
+              volumes = {
+                config.persistentVolumeClaim.claimName = "bazarr";
+                media.persistentVolumeClaim.claimName = "media";
+              };
+
+              securityContext = {
+                fsGroup = 1000;
+                fsGroupChangePolicy = "OnRootMismatch";
               };
             };
           };
@@ -467,134 +420,134 @@ in {
       };
 
       services = {
-        jellyfin = lib.mkIf cfg.jellyfin.enable {
-          spec = {
-            selector = {
-              app = "media";
-              component = "jellyfin";
+        jellyfin.spec = {
+          selector = {
+            app = "media";
+            component = "jellyfin";
+          };
+
+          ports.web = {
+            port = 80;
+            targetPort = "web";
+          };
+        };
+
+        deluge.spec = {
+          type = "LoadBalancer";
+          loadBalancerIP = globals.transmissionIPv4;
+
+          selector = {
+            app = "media";
+            component = "deluge";
+          };
+
+          ports = {
+            bittorrent = {
+              port = 31780;
+              targetPort = "bittorrent";
             };
 
-            ports.web = {
+            web = {
               port = 80;
               targetPort = "web";
             };
           };
         };
 
-        deluge = lib.mkIf cfg.deluge.enable {
-          spec = {
-            type = "LoadBalancer";
-            loadBalancerIP = globals.delugeIPv4;
+        jellyseerr.spec = {
+          type = "LoadBalancer";
+          loadBalancerIP = globals.jellyseerrIPv4;
 
-            selector = {
-              app = "media";
-              component = "deluge";
-            };
+          selector = {
+            app = "media";
+            component = "jellyseerr";
+          };
 
-            ports = {
-              bittorrent = {
-                port = 31780;
-                targetPort = "bittorrent";
-              };
-
-              web = {
-                port = 80;
-                targetPort = "web";
-              };
-            };
+          ports.web = {
+            port = 80;
+            targetPort = "web";
           };
         };
 
-        jellyseerr = lib.mkIf cfg.jellyseerr.enable {
-          spec = {
-            type = "LoadBalancer";
-            loadBalancerIP = globals.jellyseerrIPv4;
+        radarr.spec = {
+          type = "LoadBalancer";
+          loadBalancerIP = globals.radarrIPv4;
 
-            selector = {
-              app = "media";
-              component = "jellyseerr";
-            };
+          selector = {
+            app = "media";
+            component = "radarr";
+          };
 
-            ports.web = {
-              port = 80;
-              targetPort = "web";
-            };
+          ports.web = {
+            port = 80;
+            targetPort = "web";
           };
         };
 
-        radarr = lib.mkIf cfg.radarr.enable {
-          spec = {
-            type = "LoadBalancer";
-            loadBalancerIP = globals.radarrIPv4;
+        prowlarr.spec = {
+          type = "LoadBalancer";
+          loadBalancerIP = globals.prowlarrIPv4;
 
-            selector = {
-              app = "media";
-              component = "radarr";
-            };
+          selector = {
+            app = "media";
+            component = "prowlarr";
+          };
 
-            ports.web = {
-              port = 80;
-              targetPort = "web";
-            };
+          ports.web = {
+            port = 80;
+            targetPort = "web";
           };
         };
 
-        prowlarr = lib.mkIf cfg.prowlarr.enable {
-          spec = {
-            type = "LoadBalancer";
-            loadBalancerIP = globals.prowlarrIPv4;
+        sonarr.spec = {
+          type = "LoadBalancer";
+          loadBalancerIP = globals.sonarrIPv4;
 
-            selector = {
-              app = "media";
-              component = "prowlarr";
-            };
+          selector = {
+            app = "media";
+            component = "sonarr";
+          };
 
-            ports.web = {
-              port = 80;
-              targetPort = "web";
-            };
+          ports.web = {
+            port = 80;
+            targetPort = "web";
           };
         };
 
-        sonarr = lib.mkIf cfg.sonarr.enable {
-          spec = {
-            type = "LoadBalancer";
-            loadBalancerIP = globals.sonarrIPv4;
+        bazarr.spec = {
+          type = "LoadBalancer";
+          loadBalancerIP = globals.bazarrIPv4;
 
-            selector = {
-              app = "media";
-              component = "sonarr";
-            };
+          selector = {
+            app = "media";
+            component = "bazarr";
+          };
 
-            ports.web = {
-              port = 80;
-              targetPort = "web";
-            };
+          ports.web = {
+            port = 80;
+            targetPort = "web";
           };
         };
+      };
 
-        bazarr = lib.mkIf cfg.bazarr.enable {
-          spec = {
-            type = "LoadBalancer";
-            loadBalancerIP = globals.bazarrIPv4;
+      persistentVolumeClaims = {
+        jellyfin-cache.spec = {
+          accessModes = ["ReadWriteOnce"];
+          resources.requests.storage = "20Gi";
+        };
 
-            selector = {
-              app = "media";
-              component = "bazarr";
-            };
-
-            ports.web = {
-              port = 80;
-              targetPort = "web";
-            };
-          };
+        media.spec = {
+          accessModes = ["ReadWriteMany"];
+          storageClassName = "";
+          resources.requests.storage = "1Mi";
+          volumeName = "media-media";
         };
       };
     };
 
     lab = {
       ingresses = {
-        jellyfin = lib.mkIf cfg.jellyfin.enable {
+        jellyfin = {
           host = "media.kun.is";
 
           service = {
@@ -603,7 +556,7 @@ in {
           };
         };
 
-        jellyseerr = lib.mkIf cfg.jellyseerr.enable {
+        jellyseerr = {
           host = "jellyseerr.kun.is";
           entrypoint = "localsecure";
 
@@ -615,36 +568,73 @@ in {
       };
 
       tailscaleIngresses = {
-        tailscale-jellyseerr = lib.mkIf cfg.jellyseerr.enable {
+        tailscale-jellyseerr = {
           host = "jellyseerr";
           service.name = "jellyseerr";
         };
 
-        tailscale-radarr = lib.mkIf cfg.radarr.enable {
+        tailscale-radarr = {
           host = "radarr";
           service.name = "radarr";
         };
 
-        tailscale-sonarr = lib.mkIf cfg.sonarr.enable {
+        tailscale-sonarr = {
           host = "sonarr";
           service.name = "sonarr";
         };
 
-        tailscale-bazarr = lib.mkIf cfg.bazarr.enable {
+        tailscale-bazarr = {
           host = "bazarr";
           service.name = "bazarr";
         };
 
-        tailscale-prowlarr = lib.mkIf cfg.prowlarr.enable {
+        tailscale-prowlarr = {
           host = "prowlarr";
           service.name = "prowlarr";
         };
 
-        tailscale-deluge = lib.mkIf cfg.deluge.enable {
+        tailscale-deluge = {
           host = "deluge";
           service.name = "deluge";
         };
       };
+
+      longhorn.persistentVolumeClaim = {
+        jellyfin = {
+          volumeName = "jellyfin";
+          storage = "5Gi";
+        };
+
+        deluge = {
+          volumeName = "deluge";
+          storage = "500Mi";
+        };
+
+        jellyseerr = {
+          volumeName = "jellyseerr";
+          storage = "75Mi";
+        };
+
+        radarr = {
+          volumeName = "radarr";
+          storage = "300Mi";
+        };
+
+        prowlarr = {
+          volumeName = "prowlarr";
+          storage = "150Mi";
+        };
+
+        sonarr = {
+          volumeName = "sonarr";
+          storage = "150Mi";
+        };
+
+        bazarr = {
+          volumeName = "bazarr";
+          storage = "25Mi";
+        };
+      };
     };
   };
 }
diff --git a/modules/minecraft.nix b/modules/minecraft.nix
new file mode 100644
index 0000000..3e0a8f9
--- /dev/null
+++ b/modules/minecraft.nix
@@ -0,0 +1,59 @@
+{
+  lib,
+  config,
+  globals,
+  ...
+}: {
+  options.minecraft.enable = lib.mkEnableOption "minecraft";
+
+  config = lib.mkIf config.minecraft.enable {
+    kubernetes.resources = {
+      deployments.minecraft.spec = {
+        selector.matchLabels.app = "minecraft";
+
+        template = {
+          metadata.labels.app = "minecraft";
+
+          spec = {
+            volumes.data.persistentVolumeClaim.claimName = "data";
+
+            containers.minecraft = {
+              image = globals.images.minecraft;
+              ports.minecraft.containerPort = 25565;
+
+              env.EULA.value = "TRUE";
+
+              volumeMounts = [
+                {
+                  name = "data";
+                  mountPath = "/data";
+                }
+              ];
+            };
+
+            securityContext = {
+              fsGroup = 1000;
+              fsGroupChangePolicy = "OnRootMismatch";
+            };
+          };
+        };
+      };
+
+      services.minecraft.spec = {
+        type = "LoadBalancer";
+        loadBalancerIP = globals.minecraftIPv4;
+        selector.app = "minecraft";
+
+        ports.minecraft = {
+          port = 25565;
+          targetPort = "minecraft";
+        };
+      };
+    };
+
+    lab.longhorn.persistentVolumeClaim.data = {
+      volumeName = "minecraft";
+      storage = "1Gi";
+    };
+  };
+}
diff --git a/modules/nextcloud.nix b/modules/nextcloud.nix
index fa73438..11df1fd 100644
--- a/modules/nextcloud.nix
+++ b/modules/nextcloud.nix
@@ -33,12 +33,7 @@
             };
 
             spec = {
-              nodeName = "atlas";
-
-              volumes.data.hostPath = {
-                path = "/mnt/longhorn/persistent/volumes/nextcloud";
-                type = "Directory";
-              };
+              volumes.data.persistentVolumeClaim.claimName = "data";
 
               containers.nextcloud = {
                 image = globals.images.nextcloud;
@@ -106,8 +101,6 @@
             };
 
             spec = {
-              nodeName = "atlas";
-
               containers.postgres = {
                 image = globals.images.postgres15;
                 imagePullPolicy = "IfNotPresent";
@@ -132,10 +125,7 @@
                 ];
               };
 
-              volumes.database.hostPath = {
-                path = "/mnt/longhorn/persistent/volumes/nextcloud-db";
-                type = "Directory";
-              };
+              volumes.database.persistentVolumeClaim.claimName = "database";
             };
           };
         };
@@ -176,6 +166,18 @@
         host = "nextcloud";
         service.name = "server";
       };
+
+      longhorn.persistentVolumeClaim = {
+        data = {
+          volumeName = "nextcloud";
+          storage = "50Gi";
+        };
+
+        database = {
+          volumeName = "nextcloud-db";
+          storage = "400Mi";
+        };
+      };
     };
   };
 }
diff --git a/modules/ntfy.nix b/modules/ntfy.nix
index 17027c9..d970fbe 100644
--- a/modules/ntfy.nix
+++ b/modules/ntfy.nix
@@ -24,8 +24,6 @@
           metadata.labels.app = "ntfy";
 
           spec = {
-            nodeName = "jefke";
-
             containers.ntfy = {
               image = utils.mkNixNGImage "ntfy";
               ports.web.containerPort = 80;
@@ -48,30 +46,26 @@
             };
 
             volumes = {
-              data.hostPath = {
-                path = "/mnt/longhorn/persistent/volumes/ntfy";
-                type = "Directory";
-              };
-
-              cache.hostPath = {
-                path = "/tmp/ntfy-cache";
-                type = "DirectoryOrCreate";
-              };
-
-              attachment-cache.hostPath = {
-                path = "/tmp/ntfy-attachment-cache";
-                type = "DirectoryOrCreate";
-              };
-            };
-
-            securityContext = {
-              fsGroup = 407;
-              fsGroupChangePolicy = "Always";
+              cache.persistentVolumeClaim.claimName = "cache";
+              attachment-cache.persistentVolumeClaim.claimName = "attachment-cache";
+              data.persistentVolumeClaim.claimName = "data";
             };
           };
         };
       };
 
+      persistentVolumeClaims = {
+        cache.spec = {
+          accessModes = ["ReadWriteOnce"];
+          resources.requests.storage = "300Mi";
+        };
+
+        attachment-cache.spec = {
+          accessModes = ["ReadWriteOnce"];
+          resources.requests.storage = "500Mi";
+        };
+      };
+
       services.ntfy.spec = {
         selector.app = "ntfy";
 
@@ -91,6 +85,11 @@
           portName = "web";
         };
       };
+
+      longhorn.persistentVolumeClaim.data = {
+        volumeName = "ntfy";
+        storage = "300Mi";
+      };
     };
   };
 }
diff --git a/modules/paperless.nix b/modules/paperless.nix
index aea76f1..5ac3937 100644
--- a/modules/paperless.nix
+++ b/modules/paperless.nix
@@ -36,12 +36,7 @@
             };
 
             spec = {
-              nodeName = "jefke";
-
-              volumes.data.hostPath = {
-                path = "/mnt/longhorn/persistent/volumes/paperless-data";
-                type = "Directory";
-              };
+              volumes.data.persistentVolumeClaim.claimName = "data";
 
               containers.paperless = {
                 image = globals.images.paperless;
@@ -62,25 +57,6 @@
                   PAPERLESS_OCR_LANGUAGE.value = "nld";
                   USERMAP_UID.value = "33";
                   USERMAP_GID.value = "33";
-                  PAPERLESS_APPS.value = "allauth.socialaccount.providers.openid_connect";
-                  PAPERLESS_SOCIALACCOUNT_PROVIDERS.value = ''
-                    {
-                      "openid_connect": {
-                        "APPS": [
-                          {
-                            "provider_id": "authentik",
-                            "name": "Authentik",
-                            "client_id": "z5PlhxTB1eXJ9L39Ix2BfhLV72TbF3vbGJZUtyBJ",
-                            "secret": "ref+sops://secrets.yml#/authentik/oauth2/paperless-ngx/client_secret+",
-                            "settings": {
-                              "server_url": "https://authentik.kun.is/application/o/paperless-ngx/.well-known/openid-configuration"
-                            }
-                          }
-                        ],
-                        "OAUTH_PKCE_ENABLED": "True"
-                      }
-                    }
-                  '';
 
                   PAPERLESS_DBPASS.valueFrom.secretKeyRef = {
                     name = "database";
@@ -131,12 +107,7 @@
             };
 
             spec = {
-              nodeName = "jefke";
-
-              volumes.data.hostPath = {
-                path = "/mnt/longhorn/persistent/volumes/paperless-redisdata";
-                type = "Directory";
-              };
+              volumes.data.persistentVolumeClaim.claimName = "redisdata";
 
               containers.redis = {
                 image = globals.images.redis7;
@@ -181,13 +152,6 @@
             };
 
             spec = {
-              nodeName = "jefke";
-
-              volumes.data.hostPath = {
-                path = "/mnt/longhorn/persistent/volumes/paperless-db";
-                type = "Directory";
-              };
-
               containers.postgres = {
                 image = globals.images.postgres15;
                 ports.postgres.containerPort = 5432;
@@ -211,6 +175,8 @@
                   }
                 ];
               };
+
+              volumes.data.persistentVolumeClaim.claimName = "database";
             };
           };
         };
@@ -263,6 +229,23 @@
         host = "paperless";
         service.name = "web";
       };
+
+      longhorn.persistentVolumeClaim = {
+        data = {
+          volumeName = "paperless-data";
+          storage = "10Gi";
+        };
+
+        redisdata = {
+          volumeName = "paperless-redisdata";
+          storage = "20Mi";
+        };
+
+        database = {
+          volumeName = "paperless-db";
+          storage = "150Mi";
+        };
+      };
     };
   };
 }
diff --git a/modules/pihole.nix b/modules/pihole.nix
new file mode 100644
index 0000000..c437325
--- /dev/null
+++ b/modules/pihole.nix
@@ -0,0 +1,117 @@
+{
+  globals,
+  config,
+  lib,
+  ...
+}: {
+  options.pihole.enable = lib.mkEnableOption "pihole";
+
+  config = lib.mkIf config.pihole.enable {
+    kubernetes.resources = {
+      secrets.pihole.stringData.webPassword = "ref+sops://secrets.yml#/pihole/password";
+
+      deployments.pihole.spec = {
+        selector.matchLabels.app = "pihole";
+
+        strategy = {
+          type = "RollingUpdate";
+
+          rollingUpdate = {
+            maxSurge = 0;
+            maxUnavailable = 1;
+          };
+        };
+
+        template = {
+          metadata.labels.app = "pihole";
+
+          spec = {
+            containers.pihole = {
+              image = globals.images.pihole;
+
+              env = {
+                TZ.value = "Europe/Amsterdam";
+                PIHOLE_DNS_.value = "192.168.30.1";
+
+                WEBPASSWORD.valueFrom.secretKeyRef = {
+                  name = "pihole";
+                  key = "webPassword";
+                };
+              };
+
+              ports = {
+                web.containerPort = 80;
+
+                dns = {
+                  containerPort = 53;
+                  protocol = "UDP";
+                };
+              };
+
+              volumeMounts = [
+                {
+                  name = "data";
+                  mountPath = "/etc/pihole";
+                }
+                {
+                  name = "dnsmasq";
+                  mountPath = "/etc/dnsmasq.d";
+                }
+              ];
+            };
+
+            volumes = {
+              data.persistentVolumeClaim.claimName = "pihole-data";
+              dnsmasq.persistentVolumeClaim.claimName = "pihole-dnsmasq";
+            };
+
+            securityContext = {
+              fsGroup = 1000;
+              fsGroupChangePolicy = "OnRootMismatch";
+            };
+          };
+        };
+      };
+
+      services = {
+        pihole.spec = {
+          type = "LoadBalancer";
+          loadBalancerIP = globals.piholeIPv4;
+          selector.app = "pihole";
+
+          ports = {
+            dns = {
+              protocol = "UDP";
+              port = 53;
+              targetPort = "dns";
+            };
+
+            web = {
+              port = 80;
+              targetPort = "web";
+            };
+          };
+        };
+      };
+    };
+
+    lab = {
+      longhorn.persistentVolumeClaim = {
+        pihole-data = {
+          volumeName = "pihole-data";
+          storage = "750Mi";
+        };
+
+        pihole-dnsmasq = {
+          volumeName = "pihole-dnsmasq";
+          storage = "16Mi";
+        };
+      };
+
+      tailscaleIngresses.tailscale-pihole = {
+        host = "pihole";
+        service.name = "pihole";
+      };
+    };
+  };
+}
diff --git a/modules/radicale.nix b/modules/radicale.nix
index c1adcc9..8f2bb6e 100644
--- a/modules/radicale.nix
+++ b/modules/radicale.nix
@@ -25,8 +25,6 @@
           metadata.labels.app = "radicale";
 
           spec = {
-            nodeName = "jefke";
-
             containers.radicale = {
               image = utils.mkNixNGImage "radicale";
               ports.web.containerPort = 5232;
@@ -40,10 +38,7 @@
               ];
             };
 
-            volumes.data.hostPath = {
-              path = "/mnt/longhorn/persistent/volumes/radicale";
-              type = "Directory";
-            };
+            volumes.data.persistentVolumeClaim.claimName = "data";
           };
         };
       };
@@ -65,6 +60,11 @@
         host = "radicale";
         service.name = "server";
       };
+
+      longhorn.persistentVolumeClaim.data = {
+        volumeName = "radicale";
+        storage = "200Mi";
+      };
     };
   };
 }
diff --git a/modules/syncthing.nix b/modules/syncthing.nix
index 6aeb3f0..c06b81a 100644
--- a/modules/syncthing.nix
+++ b/modules/syncthing.nix
@@ -8,6 +8,8 @@
 
   config = lib.mkIf config.syncthing.enable {
     kubernetes.resources = {
+      serviceAccounts.syncthing = {};
+
       deployments.syncthing.spec = {
         selector.matchLabels.app = "syncthing";
 
@@ -24,7 +26,7 @@
           metadata.labels.app = "syncthing";
 
           spec = {
-            nodeName = "jefke";
+            serviceAccountName = "syncthing";
 
             containers.syncthing = {
               image = globals.images.syncthing;
@@ -43,22 +45,15 @@
                   mountPath = "/config";
                 }
                 {
-                  name = "keepassxc";
-                  mountPath = "/keepassxc";
+                  name = "music";
+                  mountPath = "/music";
                 }
               ];
             };
 
             volumes = {
-              keepassxc.hostPath = {
-                path = "/mnt/longhorn/persistent/volumes/keepassxc";
-                type = "Directory";
-              };
-
-              config.hostPath = {
-                path = "/mnt/longhorn/persistent/volumes/syncthing";
-                type = "Directory";
-              };
+              config.persistentVolumeClaim.claimName = "config";
+              music.persistentVolumeClaim.claimName = "music";
             };
 
             securityContext = {
@@ -79,9 +74,21 @@
           targetPort = "web";
         };
       };
+
+      persistentVolumeClaims.music.spec = {
+        accessModes = ["ReadWriteMany"];
+        storageClassName = "";
+        resources.requests.storage = "1Mi";
+        volumeName = "music-syncthing";
+      };
     };
 
     lab = {
+      longhorn.persistentVolumeClaim.config = {
+        volumeName = "syncthing";
+        storage = "400Mi";
+      };
+
       tailscaleIngresses.tailscale = {
         host = "syncthing";
         service.name = "syncthing";
diff --git a/modules/tailscale-ingress.nix b/modules/tailscale-ingress.nix
index 23eedb3..83e3f96 100644
--- a/modules/tailscale-ingress.nix
+++ b/modules/tailscale-ingress.nix
@@ -42,7 +42,7 @@
                 pathType = "Prefix";
 
                 backend.service = {
-                  inherit (service) name;
+                  name = service.name;
                   port.name = service.portName;
                 };
               }
diff --git a/modules/traefik.nix b/modules/traefik.nix
index e512b99..b59253d 100644
--- a/modules/traefik.nix
+++ b/modules/traefik.nix
@@ -20,7 +20,7 @@
             ports = {
               localsecure = {
                 port = 8444;
-                expose.default = true;
+                expose = true;
                 exposedPort = 444;
                 protocol = "TCP";
 
diff --git a/nixng-configurations/atuin.nix b/nixng-configurations/atuin.nix
deleted file mode 100644
index 189895e..0000000
--- a/nixng-configurations/atuin.nix
+++ /dev/null
@@ -1,12 +0,0 @@
-{
-  dinit.enable = true;
-  init.services.atuin.shutdownOnExit = true;
-
-  services.atuin = {
-    enable = true;
-
-    settings = {
-      open_registration = false;
-    };
-  };
-}
diff --git a/nixng-configurations/bazarr.nix b/nixng-configurations/bazarr.nix
index 703ee94..03d7b6a 100644
--- a/nixng-configurations/bazarr.nix
+++ b/nixng-configurations/bazarr.nix
@@ -1,23 +1,9 @@
-{
-  lib,
-  nglib,
-  config,
-  ...
-}: {
+{...}: {
   dinit.enable = true;
-
-  init.services.bazarr = {
-    shutdownOnExit = true;
-    group = lib.mkForce "media";
-  };
+  init.services.bazarr.shutdownOnExit = true;
 
   services.bazarr = {
     enable = true;
     configDir = "/config";
   };
-
-  users.groups.media = nglib.mkDefaultRec {
-    gid = config.ids.gids.media;
-    members = ["bazarr"];
-  };
 }
diff --git a/nixng-configurations/default.nix b/nixng-configurations/default.nix
index 852aab4..80375cb 100644
--- a/nixng-configurations/default.nix
+++ b/nixng-configurations/default.nix
@@ -5,6 +5,9 @@
   nginx,
   blog,
   nixpkgs,
+  nixpkgs-jellyseerr,
+  nixpkgs-bazarr,
+  nixpkgs-radicale,
   nixpkgs-master,
   ...
 }:
@@ -20,9 +23,7 @@ flake-utils.lib.eachDefaultSystem (system: let
     bazarr = ./bazarr.nix;
     prowlarr = ./prowlarr.nix;
     blog = ./blog.nix;
-    deluge = ./deluge.nix;
-    mealie = ./mealie.nix;
-    atuin = ./atuin.nix;
+    generic-device-plugin = ./generic-device-plugin.nix;
   };
 in {
   nixngConfigurations = builtins.mapAttrs (name: configFile:
@@ -37,21 +38,16 @@ in {
       };
 
       extraModules = [
-        self.nixngModules.ids
-        self.nixngModules.bazarr
-        self.nixngModules.radicale
-        self.nixngModules.jellyseerr
-        self.nixngModules.radarr
-        self.nixngModules.sonarr
-        self.nixngModules.prowlarr
-        self.nixngModules.deluge
-        self.nixngModules.mealie
-        self.nixngModules.atuin
         {
           nixpkgs.overlays = [
-            (_final: _prev: {
+            (final: _prev: {
               # From master branch
-              inherit (nixpkgs-master.legacyPackages.${system}) jellyseerr radicale bazarr;
+              prowlarr = nixpkgs-master.legacyPackages.${system}.prowlarr;
+
+              # From forks
+              bazarr = nixpkgs-bazarr.legacyPackages.${system}.bazarr;
+              jellyseerr = nixpkgs-jellyseerr.legacyPackages.${system}.jellyseerr;
+              radicale = nixpkgs-radicale.legacyPackages.${system}.radicale;
             })
           ];
         }
diff --git a/nixng-configurations/deluge.nix b/nixng-configurations/deluge.nix
deleted file mode 100644
index 3f44f37..0000000
--- a/nixng-configurations/deluge.nix
+++ /dev/null
@@ -1,30 +0,0 @@
-{
-  config,
-  nglib,
-  lib,
-  ...
-}: {
-  dinit.enable = true;
-  init.services = {
-    deluged = {
-      shutdownOnExit = true;
-      group = lib.mkForce "media";
-    };
-
-    deluge-web = {
-      shutdownOnExit = true;
-      group = lib.mkForce "media";
-    };
-  };
-
-  services.deluge = {
-    enable = true;
-    configDir = "/config";
-    web.enable = true;
-  };
-
-  users.groups.media = nglib.mkDefaultRec {
-    gid = config.ids.gids.media;
-    members = ["deluge"];
-  };
-}
diff --git a/nixng-configurations/generic-device-plugin.nix b/nixng-configurations/generic-device-plugin.nix
new file mode 100644
index 0000000..abb2406
--- /dev/null
+++ b/nixng-configurations/generic-device-plugin.nix
@@ -0,0 +1,10 @@
+{globals, ...}: {
+  dinit.enable = true;
+  init.services.generic-device-plugin.shutdownOnExit = true;
+
+  services.generic-device-plugin = {
+    enable = true;
+    settings = {
+    };
+  };
+}
diff --git a/nixng-configurations/jellyseerr.nix b/nixng-configurations/jellyseerr.nix
index 034306c..b86802e 100644
--- a/nixng-configurations/jellyseerr.nix
+++ b/nixng-configurations/jellyseerr.nix
@@ -1,22 +1,9 @@
-{
-  config,
-  lib,
-  nglib,
-  ...
-}: {
+{...}: {
   dinit.enable = true;
-  init.services.jellyseerr = {
-    shutdownOnExit = true;
-    group = lib.mkForce "media";
-  };
+  init.services.jellyseerr.shutdownOnExit = true;
 
   services.jellyseerr = {
     enable = true;
     configDir = "/app/config";
   };
-
-  users.groups.media = nglib.mkDefaultRec {
-    gid = config.ids.gids.media;
-    members = ["jellyseerr"];
-  };
 }
diff --git a/nixng-configurations/mealie.nix b/nixng-configurations/mealie.nix
deleted file mode 100644
index bd3350c..0000000
--- a/nixng-configurations/mealie.nix
+++ /dev/null
@@ -1,25 +0,0 @@
-{
-  dinit.enable = true;
-  init.services.mealie.shutdownOnExit = true;
-
-  services.mealie = {
-    enable = true;
-
-    settings = {
-      DATA_DIR = "/data";
-      BASE_URL = "https://mealie.kun.is";
-      ALLOW_SIGNUP = "False";
-      SMTP_HOST = "mail.smtp2go.com";
-      SMTP_PORT = "2525";
-      SMTP_FROM_NAME = "Mealie";
-      SMTP_AUTH_STRATEGY = "ssl";
-      SMTP_FROM_EMAIL = "mealie@kun.is";
-      OIDC_AUTH_ENABLED = "True";
-      OIDC_CONFIGURATION_URL = "https://authentik.kun.is/application/o/mealie/.well-known/openid-configuration";
-      OIDC_CLIENT_ID = "lvkHoIPacUXjY4jr9YyEQC7YyhccOH0atbpOiKmG";
-      OIDC_AUTO_REDIRECT = "True";
-      OIDC_PROVIDER_NAME = "Authentik";
-      OIDC_REMEMBER_ME = "True";
-    };
-  };
-}
diff --git a/nixng-configurations/ntfy.nix b/nixng-configurations/ntfy.nix
index a957c42..7fc7ef1 100644
--- a/nixng-configurations/ntfy.nix
+++ b/nixng-configurations/ntfy.nix
@@ -1,4 +1,4 @@
-{
+{...}: {
   dinit.enable = true;
   init.services.ntfy-sh.shutdownOnExit = true;
 
diff --git a/nixng-configurations/prowlarr.nix b/nixng-configurations/prowlarr.nix
index 7214747..e12e1bb 100644
--- a/nixng-configurations/prowlarr.nix
+++ b/nixng-configurations/prowlarr.nix
@@ -1,22 +1,9 @@
-{
-  lib,
-  nglib,
-  config,
-  ...
-}: {
+{...}: {
   dinit.enable = true;
-  init.services.prowlarr = {
-    shutdownOnExit = true;
-    group = lib.mkForce "media";
-  };
+  init.services.prowlarr.shutdownOnExit = true;
 
   services.prowlarr = {
     enable = true;
     dataDir = "/config";
   };
-
-  users.groups.media = nglib.mkDefaultRec {
-    gid = config.ids.gids.media;
-    members = ["prowlarr"];
-  };
 }
diff --git a/nixng-configurations/radarr.nix b/nixng-configurations/radarr.nix
index 21cd30a..c647100 100644
--- a/nixng-configurations/radarr.nix
+++ b/nixng-configurations/radarr.nix
@@ -1,22 +1,9 @@
-{
-  lib,
-  nglib,
-  config,
-  ...
-}: {
+{...}: {
   dinit.enable = true;
-  init.services.radarr = {
-    shutdownOnExit = true;
-    group = lib.mkForce "media";
-  };
+  init.services.radarr.shutdownOnExit = true;
 
   services.radarr = {
     enable = true;
     dataDir = "/config";
   };
-
-  users.groups.media = nglib.mkDefaultRec {
-    gid = config.ids.gids.media;
-    members = ["radarr"];
-  };
 }
diff --git a/nixng-configurations/sonarr.nix b/nixng-configurations/sonarr.nix
index f756fde..b98f9cf 100644
--- a/nixng-configurations/sonarr.nix
+++ b/nixng-configurations/sonarr.nix
@@ -1,22 +1,9 @@
-{
-  lib,
-  config,
-  nglib,
-  ...
-}: {
+{...}: {
   dinit.enable = true;
-  init.services.sonarr = {
-    shutdownOnExit = true;
-    group = lib.mkForce "media";
-  };
+  init.services.sonarr.shutdownOnExit = true;
 
   services.sonarr = {
     enable = true;
     dataDir = "/config";
   };
-
-  users.groups.media = nglib.mkDefaultRec {
-    gid = config.ids.gids.media;
-    members = ["sonarr"];
-  };
 }
diff --git a/nixng-modules/atuin.nix b/nixng-modules/atuin.nix
deleted file mode 100644
index 914f5a7..0000000
--- a/nixng-modules/atuin.nix
+++ /dev/null
@@ -1,86 +0,0 @@
-{
-  pkgs,
-  lib,
-  nglib,
-  config,
-  ...
-}: let
-  cfg = config.services.atuin;
-  cfgInit = config.init.services.atuin;
-  settingsFormat = pkgs.formats.toml {};
-in {
-  options.services.atuin = {
-    enable = lib.mkEnableOption "atuin";
-    package = lib.mkPackageOption pkgs "atuin" {};
-
-    settings = lib.mkOption {
-      type = lib.types.submodule {
-        freeformType = settingsFormat.type;
-
-        options = {
-          host = lib.mkOption {
-            type = lib.types.str;
-            default = "0.0.0.0";
-            description = "The host to listen on";
-          };
-
-          port = lib.mkOption {
-            type = lib.types.port;
-            default = 8888;
-            description = "The TCP port to listen on";
-          };
-
-          open_registration = lib.mkOption {
-            type = lib.types.bool;
-            default = false;
-            description = "If true, accept new user registrations";
-          };
-
-          db_uri = lib.mkOption {
-            type = with lib.types; nullOr str;
-            default = null;
-            description = "A valid PostgreSQL URI, for saving history";
-          };
-
-          path = lib.mkOption {
-            type = lib.types.str;
-            default = "";
-            description = "A path to prepend to all the routes of the server";
-          };
-        };
-      };
-    };
-  };
-
-  config = lib.mkIf cfg.enable {
-    init.services.atuin = {
-      enabled = true;
-      user = lib.mkDefault "atuin";
-      group = lib.mkDefault "atuin";
-
-      script = pkgs.writeShellScript "atuin-run" ''
-        ${lib.getExe cfg.package} server start
-      '';
-    };
-
-    environment = {
-      systemPackages = [cfg.package];
-
-      variables.ATUIN_CONFIG_DIR = let
-        settingsFile = settingsFormat.generate "server.toml" (lib.filterAttrs (_: v: v != null) cfg.settings);
-      in
-        toString (pkgs.writeTextDir "server.toml" (builtins.readFile settingsFile));
-    };
-
-    users.users.${cfgInit.user} = nglib.mkDefaultRec {
-      description = "atuin";
-      inherit (cfgInit) group;
-      createHome = false;
-      home = "/var/empty";
-      useDefaultShell = true;
-      uid = config.ids.uids.atuin;
-    };
-
-    users.groups.${cfgInit.group} = nglib.mkDefaultRec {gid = config.ids.gids.atuin;};
-  };
-}
diff --git a/nixng-modules/bazarr.nix b/nixng-modules/bazarr.nix
deleted file mode 100644
index c5e5ffe..0000000
--- a/nixng-modules/bazarr.nix
+++ /dev/null
@@ -1,49 +0,0 @@
-{
-  lib,
-  nglib,
-  config,
-  pkgs,
-  ...
-}: let
-  cfg = config.services.bazarr;
-  cfgInit = config.init.services.bazarr;
-in {
-  options.services.bazarr = {
-    enable = lib.mkEnableOption "bazarr";
-    package = lib.mkPackageOption pkgs "bazarr" {};
-
-    configDir = lib.mkOption {
-      description = "Where Bazarr's configuration files are stored.";
-      type = lib.types.str;
-      default = "/config";
-    };
-  };
-
-  config = lib.mkIf cfg.enable {
-    init.services.bazarr = {
-      enabled = true;
-      user = lib.mkDefault "bazarr";
-      group = lib.mkDefault "bazarr";
-
-      script = pkgs.writeShellScript "bazarr-run" ''
-        umask 0002
-        ${lib.getExe cfg.package} \
-          --no-update \
-          --config '${cfg.configDir}'
-      '';
-    };
-
-    environment.systemPackages = [cfg.package];
-
-    users.users.${cfgInit.user} = lib.mkIf (cfgInit.user == "bazarr") (nglib.mkDefaultRec {
-      description = "bazarr";
-      group = cfgInit.group;
-      createHome = false;
-      home = "/var/empty";
-      useDefaultShell = true;
-      uid = config.ids.uids.bazarr;
-    });
-
-    users.groups.${cfgInit.group} = lib.mkIf (cfgInit.group == "bazarr") (nglib.mkDefaultRec {gid = config.ids.gids.bazarr;});
-  };
-}
diff --git a/nixng-modules/default.nix b/nixng-modules/default.nix
deleted file mode 100644
index 01f9eed..0000000
--- a/nixng-modules/default.nix
+++ /dev/null
@@ -1,14 +0,0 @@
-_: {
-  nixngModules = {
-    bazarr = import ./bazarr.nix;
-    radicale = import ./radicale.nix;
-    jellyseerr = import ./jellyseerr.nix;
-    radarr = import ./radarr.nix;
-    sonarr = import ./sonarr.nix;
-    prowlarr = import ./prowlarr.nix;
-    ids = import ./ids.nix;
-    deluge = import ./deluge.nix;
-    mealie = import ./mealie.nix;
-    atuin = import ./atuin.nix;
-  };
-}
diff --git a/nixng-modules/deluge.nix b/nixng-modules/deluge.nix
deleted file mode 100644
index 505f4bf..0000000
--- a/nixng-modules/deluge.nix
+++ /dev/null
@@ -1,85 +0,0 @@
-{
-  lib,
-  nglib,
-  config,
-  pkgs,
-  ...
-}: let
-  cfg = config.services.deluge;
-  cfgInit = config.init.services.deluged;
-in {
-  options.services.deluge = {
-    enable = lib.mkEnableOption "deluge";
-    package = lib.mkPackageOption pkgs "deluge-2_x" {};
-
-    configDir = lib.mkOption {
-      type = lib.types.path;
-      default = "/var/lib/deluge";
-
-      description = ''
-        Directory for Deluge's run-time configuration
-      '';
-    };
-
-    web = {
-      enable = lib.mkEnableOption "Deluge web daemon";
-
-      port = lib.mkOption {
-        type = lib.types.port;
-        default = 8112;
-        description = ''
-          Deluge web UI port
-        '';
-      };
-    };
-  };
-
-  config = lib.mkIf cfg.enable {
-    init.services = {
-      deluged = {
-        enabled = true;
-        user = lib.mkDefault "deluge";
-        group = lib.mkDefault "deluge";
-        tmpfiles = with nglib.nottmpfiles.dsl; [(d cfg.configDir "-" cfgInit.user cfgInit.group _ _)];
-
-        script = pkgs.writeShellScript "deluged-run" ''
-          # TODO: make init-level option?
-          umask 0002
-          ${cfg.package}/bin/deluged \
-            --do-not-daemonize \
-            --config ${cfg.configDir}
-        '';
-      };
-
-      deluge-web = {
-        enabled = cfg.web.enable;
-        dependencies = ["deluged"];
-        user = lib.mkDefault "deluge";
-        group = lib.mkDefault "deluge";
-
-        script = pkgs.writeShellScript "deluge-web-run" ''
-          ${cfg.package}/bin/deluge-web \
-            --do-not-daemonize \
-            --port ${toString cfg.web.port} \
-            --config ${cfg.configDir}
-        '';
-      };
-    };
-
-    environment = {
-      systemPackages = [cfg.package];
-      variables.PYTHON_EGG_CACHE = "${config.users.users.${cfgInit.user}.home}/.cache";
-    };
-
-    users.users.${cfgInit.user} = lib.mkIf (cfgInit.user == "deluge") (nglib.mkDefaultRec {
-      description = "deluge";
-      inherit (cfgInit) group;
-      createHome = true;
-      home = "/home/deluge";
-      useDefaultShell = true;
-      uid = config.ids.uids.deluge;
-    });
-
-    users.groups.${cfgInit.group} = lib.mkIf (cfgInit.group == "deluge") (nglib.mkDefaultRec {gid = config.ids.gids.deluge;});
-  };
-}
diff --git a/nixng-modules/ids.nix b/nixng-modules/ids.nix
deleted file mode 100644
index 1470307..0000000
--- a/nixng-modules/ids.nix
+++ /dev/null
@@ -1,28 +0,0 @@
-{
-  ids = {
-    uids = {
-      radicale = 408;
-      jellyseerr = 409;
-      radarr = 410;
-      sonarr = 411;
-      bazarr = 412;
-      prowlarr = 413;
-      deluge = 414;
-      mealie = 415;
-      atuin = 416;
-    };
-
-    gids = {
-      media = 51;
-      radicale = 408;
-      jellyseerr = 409;
-      radarr = 410;
-      sonarr = 411;
-      bazarr = 412;
-      prowlarr = 413;
-      deluge = 414;
-      mealie = 415;
-      atuin = 416;
-    };
-  };
-}
diff --git a/nixng-modules/jellyseerr.nix b/nixng-modules/jellyseerr.nix
deleted file mode 100644
index 745ba4c..0000000
--- a/nixng-modules/jellyseerr.nix
+++ /dev/null
@@ -1,65 +0,0 @@
-{
-  lib,
-  nglib,
-  pkgs,
-  config,
-  ...
-}: let
-  cfg = config.services.jellyseerr;
-  cfgInit = config.init.services.jellyseerr;
-in {
-  options.services.jellyseerr = {
-    enable = lib.mkEnableOption "jellyseerr";
-    package = lib.mkPackageOption pkgs "jellyseerr" {};
-
-    port = lib.mkOption {
-      description = ''
-        The port Jellyseerr should listen on.
-      '';
-      type = lib.types.port;
-      example = 8080;
-      default = 5055;
-    };
-
-    configDir = lib.mkOption {
-      description = ''
-        The directory to save run-time configuration.
-      '';
-      type = lib.types.str;
-      example = "/jellyseerr";
-      default = "/var/lib/jellyseerr";
-    };
-  };
-
-  config = lib.mkIf cfg.enable {
-    init.services.jellyseerr = {
-      enabled = true;
-      script = pkgs.writeShellScript "jellyseerr-run" ''
-        umask 0002
-        ${lib.getExe cfg.package}
-      '';
-      user = lib.mkDefault "jellyseerr";
-      group = lib.mkDefault "jellyseerr";
-    };
-
-    environment = {
-      systemPackages = [cfg.package];
-
-      variables = {
-        PORT = builtins.toString cfg.port;
-        CONFIG_DIRECTORY = cfg.configDir;
-      };
-    };
-
-    users.users.${cfgInit.user} = lib.mkIf (cfgInit.user == "jellyseerr") (nglib.mkDefaultRec {
-      description = "jellyseerr";
-      group = cfgInit.group;
-      createHome = false;
-      home = "/var/empty";
-      useDefaultShell = true;
-      uid = config.ids.uids.jellyseerr;
-    });
-
-    users.groups.${cfgInit.group} = lib.mkIf (cfgInit.group == "jellyseerr") (nglib.mkDefaultRec {gid = config.ids.gids.jellyseerr;});
-  };
-}
diff --git a/nixng-modules/mealie.nix b/nixng-modules/mealie.nix
deleted file mode 100644
index 583d4b4..0000000
--- a/nixng-modules/mealie.nix
+++ /dev/null
@@ -1,85 +0,0 @@
-{
-  lib,
-  nglib,
-  pkgs,
-  config,
-  ...
-}: let
-  cfg = config.services.mealie;
-  cfgInit = config.init.services.mealie;
-in {
-  options.services.mealie = {
-    enable = lib.mkEnableOption "mealie";
-    package = lib.mkPackageOption pkgs "mealie" {};
-    settings = lib.mkOption {
-      type = lib.types.submodule {
-        freeformType = with lib.types; attrsOf str;
-
-        options = {
-          PRODUCTION = lib.mkOption {
-            type = lib.types.str;
-            default = "true";
-          };
-
-          DATA_DIR = lib.mkOption {
-            type = with lib.types; nullOr str;
-            default = null;
-          };
-
-          DB_ENGINE = lib.mkOption {
-            type = with lib.types; nullOr str;
-            default = "sqlite";
-          };
-
-          ALEMBIC_CONFIG_FILE = lib.mkOption {
-            type = with lib.types; nullOr str;
-            default = "${cfg.package}/alembic.ini";
-          };
-        };
-      };
-
-      description = ''
-        Configuration of the Mealie service.
-
-        See [the Mealie documentation](https://nightly.mealie.io/documentation/getting-started/installation/backend-config/) for available options and default values.
-      '';
-      default = {};
-    };
-  };
-
-  config = lib.mkIf cfg.enable {
-    init.services.mealie = {
-      enabled = true;
-      user = lib.mkDefault "mealie";
-      group = lib.mkDefault "mealie";
-
-      tmpfiles = with nglib.nottmpfiles.dsl; lib.optional (cfg.settings.DATA_DIR != null) (d "${cfg.settings.DATA_DIR}" "-" cfgInit.user cfgInit.group "-" _);
-
-      execStart =
-        pkgs.writeShellScript "mealie-run"
-        (let
-          # Mealie can only be configured via environmental variables.
-          # With this, we don't accidentally overwrite env variables set by the user.
-          extraEnvLines = lib.mapAttrsToList (key: value: ''export ${key}=''${${key}:=${value}}'') cfg.settings;
-        in ''
-          ${lib.concatStringsSep "\n" extraEnvLines}
-          ${cfg.package}/libexec/init_db
-
-          ${lib.getExe cfg.package} -b 0.0.0.0:8000
-        '');
-    };
-
-    environment.systemPackages = [cfg.package];
-
-    users.users.${cfgInit.user} = nglib.mkDefaultRec {
-      description = "mealie";
-      inherit (cfgInit) group;
-      createHome = false;
-      home = "/var/empty";
-      useDefaultShell = true;
-      uid = config.ids.uids.mealie;
-    };
-
-    users.groups.${cfgInit.group} = nglib.mkDefaultRec {gid = config.ids.gids.mealie;};
-  };
-}
diff --git a/nixng-modules/prowlarr.nix b/nixng-modules/prowlarr.nix
deleted file mode 100644
index cf79cea..0000000
--- a/nixng-modules/prowlarr.nix
+++ /dev/null
@@ -1,48 +0,0 @@
-{
-  pkgs,
-  lib,
-  nglib,
-  config,
-  ...
-}: let
-  cfg = config.services.prowlarr;
-  cfgInit = config.init.services.prowlarr;
-in {
-  options.services.prowlarr = {
-    enable = lib.mkEnableOption "prowlarr";
-    package = lib.mkPackageOption pkgs "prowlarr" {};
-
-    dataDir = lib.mkOption {
-      description = "Directory to store Prowlarr's data";
-      type = lib.types.str;
-      default = "/config";
-    };
-  };
-
-  config = lib.mkIf cfg.enable {
-    init.services.prowlarr = {
-      enabled = true;
-      user = lib.mkDefault "prowlarr";
-      group = lib.mkDefault "prowlarr";
-
-      script = pkgs.writeShellScript "prowlarr-run" ''
-        ${lib.getExe cfg.package} \
-          -nobrowser \
-          -data=${cfg.dataDir}
-      '';
-    };
-
-    environment.systemPackages = [cfg.package];
-
-    users.users.${cfgInit.user} = lib.mkIf (cfgInit.user == "prowlarr") (nglib.mkDefaultRec {
-      description = "prowlarr";
-      group = cfgInit.group;
-      createHome = false;
-      home = "/var/empty";
-      useDefaultShell = true;
-      uid = config.ids.uids.prowlarr;
-    });
-
-    users.groups.${cfgInit.group} = lib.mkIf (cfgInit.group == "prowlarr") (nglib.mkDefaultRec {gid = config.ids.gids.prowlarr;});
-  };
-}
diff --git a/nixng-modules/radarr.nix b/nixng-modules/radarr.nix
deleted file mode 100644
index 8ea2b39..0000000
--- a/nixng-modules/radarr.nix
+++ /dev/null
@@ -1,47 +0,0 @@
-{
-  config,
-  lib,
-  nglib,
-  pkgs,
-  ...
-}: let
-  cfg = config.services.radarr;
-  cfgInit = config.init.services.radarr;
-in {
-  options.services.radarr = {
-    enable = lib.mkEnableOption "radarr";
-    package = lib.mkPackageOption pkgs "radarr" {};
-
-    dataDir = lib.mkOption {
-      type = lib.types.str;
-      default = "/var/lib/radarr/.config/Radarr";
-      description = "The directory where Radarr stores its data files.";
-    };
-  };
-
-  config = lib.mkIf cfg.enable {
-    init.services.radarr = {
-      enabled = true;
-      user = lib.mkDefault "radarr";
-      group = lib.mkDefault "radarr";
-
-      script = pkgs.writeShellScript "radarr-run.sh" ''
-        umask 0002
-        ${lib.getExe cfg.package} -nobrowser -data='${cfg.dataDir}'
-      '';
-    };
-
-    environment.systemPackages = [cfg.package];
-
-    users.users.${cfgInit.user} = lib.mkIf (cfgInit.user == "radarr") (nglib.mkDefaultRec {
-      description = "radarr";
-      inherit (cfgInit) group;
-      createHome = false;
-      home = "/var/empty";
-      useDefaultShell = true;
-      uid = config.ids.uids.radarr;
-    });
-
-    users.groups.${cfgInit.group} = lib.mkIf (cfgInit.group == "radarr") (nglib.mkDefaultRec {gid = config.ids.gids.radarr;});
-  };
-}
diff --git a/nixng-modules/radicale.nix b/nixng-modules/radicale.nix
deleted file mode 100644
index 80dd38a..0000000
--- a/nixng-modules/radicale.nix
+++ /dev/null
@@ -1,71 +0,0 @@
-{
-  lib,
-  pkgs,
-  config,
-  nglib,
-  ...
-}: let
-  cfg = config.services.radicale;
-  cfgInit = config.init.services.radicale;
-
-  settingsFormat = pkgs.formats.ini {
-    listToValue = lib.concatMapStringsSep ", " (lib.generators.mkValueStringDefault {});
-  };
-in {
-  options.services.radicale = {
-    enable = lib.mkEnableOption "radicale";
-    package = lib.mkPackageOption pkgs "radicale" {};
-
-    settings = lib.mkOption {
-      type = settingsFormat.type;
-      default = {};
-
-      description = ''
-        Configuration for Radicale. See
-        <https://radicale.org/v3.html#configuration>.
-      '';
-
-      example = lib.literalExpression ''
-        server = {
-          hosts = [ "0.0.0.0:5232" "[::]:5232" ];
-        };
-        auth = {
-          type = "htpasswd";
-          htpasswd_filename = "/etc/radicale/users";
-          htpasswd_encryption = "bcrypt";
-        };
-        storage = {
-          filesystem_folder = "/var/lib/radicale/collections";
-        };
-      '';
-    };
-  };
-
-  config = lib.mkIf cfg.enable (let
-    configFile = settingsFormat.generate "radicale.ini" cfg.settings;
-  in {
-    init.services.radicale = {
-      enabled = true;
-      user = lib.mkDefault "radicale";
-      group = lib.mkDefault "radicale";
-
-      script = pkgs.writeShellScript "radicale-run" ''
-        ${cfg.package}/bin/radicale \
-          --config ${configFile}
-      '';
-    };
-
-    environment.systemPackages = [cfg.package];
-
-    users.users.${cfgInit.user} = nglib.mkDefaultRec {
-      description = "radicale";
-      group = cfgInit.group;
-      createHome = false;
-      home = "/var/empty";
-      useDefaultShell = true;
-      uid = config.ids.uids.radicale;
-    };
-
-    users.groups.${cfgInit.group} = nglib.mkDefaultRec {gid = config.ids.gids.radicale;};
-  });
-}
diff --git a/nixng-modules/sonarr.nix b/nixng-modules/sonarr.nix
deleted file mode 100644
index b78a4cc..0000000
--- a/nixng-modules/sonarr.nix
+++ /dev/null
@@ -1,48 +0,0 @@
-{
-  lib,
-  nglib,
-  config,
-  pkgs,
-  ...
-}: let
-  cfg = config.services.sonarr;
-  cfgInit = config.init.services.sonarr;
-in {
-  options.services.sonarr = {
-    enable = lib.mkEnableOption "sonarr";
-    package = lib.mkPackageOption pkgs "sonarr" {};
-
-    dataDir = lib.mkOption {
-      type = lib.types.str;
-      default = "/var/lib/sonarr/.config/NzbDrone";
-      description = "The directory where Sonarr stores its data files.";
-    };
-  };
-
-  config = lib.mkIf cfg.enable {
-    init.services.sonarr = {
-      enabled = true;
-      user = lib.mkDefault "sonarr";
-      group = lib.mkDefault "sonarr";
-      tmpfiles = with nglib.nottmpfiles.dsl; [(e "${cfg.dataDir}/logs" "-" cfgInit.user cfgInit.group "7d" _)];
-
-      script = pkgs.writeShellScript "sonarr-run" ''
-        umask 0002
-        ${lib.getExe cfg.package} -nobrowser -data=${cfg.dataDir}
-      '';
-    };
-
-    environment.systemPackages = [cfg.package];
-
-    users.users.${cfgInit.user} = lib.mkIf (cfgInit.user == "sonarr") (nglib.mkDefaultRec {
-      description = "sonarr";
-      inherit (cfgInit) group;
-      createHome = false;
-      home = "/var/empty";
-      useDefaultShell = true;
-      uid = config.ids.uids.sonarr;
-    });
-
-    users.groups.${cfgInit.group} = lib.mkIf (cfgInit.group == "sonarr") (nglib.mkDefaultRec {gid = config.ids.gids.sonarr;});
-  };
-}
diff --git a/scripts/gen-k3s-cert.sh b/scripts/gen-k3s-cert.sh
index 71ad441..405f9f9 100644
--- a/scripts/gen-k3s-cert.sh
+++ b/scripts/gen-k3s-cert.sh
@@ -13,11 +13,6 @@ if [ -z "$username" ] || [ -z "$host" ]
     exit 1
 fi
 
-if [ ! -d "$output_path" ]; then
-    echo "Output directory $output_path does not exist!"
-    exit 1
-fi
-
 # Create a temporary directory
 temp=$(mktemp -d)
 
diff --git a/secrets.yml b/secrets.yml
index 764c9a4..170dd31 100644
--- a/secrets.yml
+++ b/secrets.yml
@@ -1,14 +1,11 @@
 freshrss:
     password: ENC[AES256_GCM,data:ECDPrW+VgO8PY9p2fLIreRETNiRL5ZGnu/PMC7aNj8KaWfyNYL+l3w==,iv:srR/r1EtOpC/CKKrCDKcTLVdMFPAYIJIB1CCg8mS0UU=,tag:YN4PqR5uvPkVskpJWD+91g==,type:str]
-    oidc_crypto_key: ENC[AES256_GCM,data:dFQKZtFVd5l8W2go6WcK76o7O7hpQWnQKXCGTf9EhSVURvWigv6zzBULie7Y4lkJCsItG8oKmIiCYSy3MhFnU3DJTUJcenm4I7NHyINjvzHOBgUVPXbYQjQhouJwOlPkdqlSKv1f38ItZKNPJebMObZj+kACKbjdik6e6yM40RM=,iv:g6Ygval2qTQwKnrliI+n/r9OxJFePT9MKYyBLU6b3UQ=,tag:kWXTbm2JIR5aL/s4OX2Tqg==,type:str]
+pihole:
+    password: ENC[AES256_GCM,data:MA60825Tl6aYEFVoPgo8k5Vjb9zmIxtPLJriQV1B3P1bOKu1KK7vxQ==,iv:RGZHox8CbJiEEEjMo2k/tNbtjCPy/QY7vOuMN/YNZcg=,tag:yphrq03IKpXM/tSDBLeSgA==,type:str]
 hedgedoc:
     databaseURL: ENC[AES256_GCM,data:6+IV4TaClIGE1XVkUf7JwXzqx3EvWiIKFx9X5x7QKvQKC7bIieD1ADVeAMQmiQfibnH/YV5TgjNY8Ft+3eX881c3yD+2j7mM+O1fX6taK/BCokDnqhIwTN2qxHsu+mrPcM/Pgg5Zqy8HvUgX8jM=,iv:bCwuNk5CVgK2T5IgLebcKwxwloi6FkWMWhnxwJek1GM=,tag:UDQ0KmRDVlDh35Fjm6eaAA==,type:str]
     sessionSecret: ENC[AES256_GCM,data:7FdRjAShjjue1fFwizCgK+94mkbT4ohAPxdyn/8Z8/f2nvGWPZHO/hGexOixbRGLPewJSaMunTMeJL+IzFlGlg==,iv:iz7640b8Mlb6mNps20b+TbphWDEFUbKwKNUXc0kR5NY=,tag:fdEr1tbes1h8VCA/q+0sOw==,type:str]
     databasePassword: ENC[AES256_GCM,data:wdRhCluNx2IzgDqouAoIcG6yOWwNLOaEkpqgYEeFvJDZsMC8OUuV7Q==,iv:Csut0c+LRKWD2b4uVuQpGnwwVqnGT6Sk6T/ODlH57Bk=,tag:7bS6a18GyRifq2D6cIheaw==,type:str]
-    oidc:
-        client_secret:
-            password: ENC[AES256_GCM,data:rLkMFkEzCF3+ejGAUliaBMpfOrxL5b3pJVvkblEFIvHupmr9DTS71L4T6/oo616E4IoCuZxqKh0FxhdWZRM4KrEk5YZMbgp9,iv:TbySrCTY+Kps4v/q5maQglm4aOzuUPch2ECBHPp4FYc=,tag:mz71m6JqIemVCypf93hkBQ==,type:str]
-            digest: ENC[AES256_GCM,data:m+abt7SEXZoeOA+ioB3BmiWN09CDpCWdZ5eNRhShvuUJW7rjbSJAQ2NTZkMTnFoNv6PO6rSUD6lmStFfO/mqVUFvWXBBDMK3xK+ILcgDoZZ1Pnmr19noqTL5OxyYLAtUHRNOohsUBYlZKyv2lFNQlqxk6kOi+fUPitmmsO6BO1nrq2s=,iv:CmC0JLXuT9O29Rtu7Pp8i0h/SyaEAT0bsXQb2LRFAkk=,tag:/IL39BCMuw+bqxv2OaNwhw==,type:str]
 nextcloud:
     databasePassword: ENC[AES256_GCM,data:jRLgW96FnMEpU0T5z/iQOX/CgjpH2ZykZGd1qGFHK8o=,iv:YrY9IsrlCaiQ8BFYqu+UnOxnvvB/JN4iYfy3vMa3wcw=,tag:41iWc4iVqjdUr02O5CLu7g==,type:str]
 paperless:
@@ -32,29 +29,6 @@ immich:
 tailscale:
     clientID: ENC[AES256_GCM,data:O8tTyy55xP85JkbJNR5daB4=,iv:SMj83Sxh7BvPRG3l5TnnpmclO5N2treUQCCJuMy8cO8=,tag:UUSN3bsZvb09cyYN65RQDg==,type:str]
     clientSecret: ENC[AES256_GCM,data:c8E/a7McI+wGN9TFJ/yzTSkrhUlISmrNJdjDDMqAQrZ8s5wFEZ+4+h+dtwcjF9Ykj198glgny7cP3HubHVDw,iv:ifaP4NmLRQbYQtJQaMMCMaehosapZ2R3im9ew5h6f9E=,tag:XF+xB94nua8RZlkGxFDFFQ==,type:str]
-authentik:
-    secret_key: ENC[AES256_GCM,data:bbEEpymADAGY/fDNMU7FfzveyK2SBUBCitQLN85tB5C5u32PRsRnOa2MDjKGU4kArnyV/WtJQXT4HJ//nMLVh53Q8BYclWYYVFourEjaajTixkZ2gkAfcMJ1mMaQG09ylrwMhLsZWbeaLFzW6dfPSQOCchvj3VhgvJSXuhNmr90=,iv:aE/DGt/yd9wL5qlWfdyT/9SIsCj7U3GcljArcGIdh9k=,tag:CVeiZITOJ71/jdLzjZjteA==,type:str]
-    postgresql_password: ENC[AES256_GCM,data:4okPqDzPDnx7ZBFQV2Jtk6SEHTskRd2GVG4XLdpMQrgivKsuhQJf1QAnCWHrjmtg74xdlUy2TdPwTWGd5UiM1G90GwHSzLPSJt6X0IFMxCuq/eyYYbD9w8Wk1pVuvqoluPcjN6WoRJdCzap1QITih8B+oSkTJ/rk84xczsjah4U=,iv:r0dYWcsIqdH8FGuBd2dxAJ1AjRmk6k4QYKq0cnZITk4=,tag:errORaXgO7yJW7ERbmdtRg==,type:str]
-    oauth2:
-        freshrss:
-            client_secret: ENC[AES256_GCM,data:e7wdwWRS8KivGkcWaMgSrUEEuOTHzj1oim+qUcLD35/DA/V6itM2XqVPhqIOXHrf6pOyYgprEv14bEx8zUvtT6iXV4fsEUEWeWTgt4NI3YULtx/t0yVDq9Zc8fN9cIqGxGeig1mcQwmm7vByq58mNJEpcfz46swjN2ATf/CPJQs=,iv:xeNgSKd+g4ne8NLw/2KQjTXSvNkqezOhMn5niuWpD38=,tag:ElOUMg0oZ+q15hCgh/Mzug==,type:str]
-        hedgedoc:
-            client_secret: ENC[AES256_GCM,data:hdNQzatO6Pf6mxvfO4h1XrhycKMBUHElEwacGttzByi4JDbIndAwYc2GXdwUmytPMYs/s+lVjcdHhspUFWS01DETWQfnWm/GN73GzW18uj3XyRXqt62HhMf18GvRlOWkGX+jYpUTGGoonYes2xijhD/mNCjxKk5Q+6FVFT2mdJ4=,iv:pScEX6YnoU7HelxmCes8A9vJjPdvFbqbclHYMme8OOE=,tag:FURxphI8IDMvOwB4ahD8hg==,type:str]
-        paperless-ngx:
-            client_secret: ENC[AES256_GCM,data:GgF+gQt8olzKUzGMDL6mh6UWDv49OPDH5tB/gboWkFd7Njc1SrSkqf71gQryOcPQ0vpXrh0nK1z6ZjMpmDEA5ohTwWymeLCgwNtJSAMHZ1VlZ2aQZr70r3KtAxKjmTiT5flUYnxS79fCF43BveSMGeAshRCvQmYCdi43sP2E4To=,iv:DzsIRPiMzxaqVrjaHMVKWgOR0asZQzWf8EE1nxRSJmk=,tag:79bo7EzVq9tvL6ap6jfV+Q==,type:str]
-        forgejo:
-            client_secret: ENC[AES256_GCM,data:I0LBIrsPuARFEcvu0sKhIbkEYxLhZrwpRfPls3KDARu5rnfwgbJ6AVtfMmcAIM9ISFzXykoyMXossHo1i23N90PsHdl2t580EffhJ+q/UUfCIk7/rX/6CXlcb8WHdab4ymN5r9jEsgD3mAWX55IehU96ZKGRKRhxSIowCIYRhyQ=,iv:1wQDGCDhSu0s+IqXULiHmRiKGTLRvOjwsYaNMCWfkjg=,tag:p1mwks0KP9lhbciTIv3/Dw==,type:str]
-        immich:
-            client_secret: ENC[AES256_GCM,data:KrsaLLsjfQsyNQzvQF/pCLj1dhi8tr/OdToY7WczvPUUQKMtSk//oxsiPike/HoVEuCUp+j7UlTfIRPF2xUcPPvw7pkcLhQhcot79aieI1ciIeLZ1Q5svsPrqDBmDY7g65jkzA9vjM9VLTsx4Dx/1vGHDqo7I12qadEQlKAuhhQ=,iv:3icAM7sVe2HlmosbP7VPbcF4SRz/mlbzdQ1gENR9TRs=,tag:O8TCN7NltNpDGoG3T8Ds1w==,type:str]
-        nextcloud:
-            client_secret: ENC[AES256_GCM,data:zLejYbfudK/4OquLXPYTv9YOmFpCVfg0KLNkDSDCpFrxroDUAXBCLtYXiGuYkYrD/t7LAzRt+OTq70d7ciuHhBNSLclP2U97BQoXCWscWnxQauRZ+UCABvP+DB9VPQmCwU+uKPrKQ8l51baj+MkpIDdk2lwavpONMU57Zov6N2o=,iv:aQ4bsXUXn177tCxe1kAsSMP9ynEzvDwN0hwFhrT3Nko=,tag:EFcnf6VmyFt2i4+aL56sWw==,type:str]
-        kitchenowl:
-            client_secret: ENC[AES256_GCM,data:x4Xsd3d3El59HKBYNV56ah314hYSRhzt46upW34cOopXNHSB3zCDrD46LUa6i8g6V5GJyrMpMfO5mv+b80JrmfHkhGUXZXuTwDNu6ijnO6ZCvC2Bdlo+T0tlkJe25OMCBseJkkC++UBrpKQQTAhyVjnPSVrGVvtY4WtdAw+X/OY=,iv:pOowIhPD7kb2F3ylFzLwNW3BhPZyzoFCGRm2+KCmhno=,tag:GxFI0w06EyGxFwj6Fv4ZLQ==,type:str]
-        mealie:
-            client_secret: ENC[AES256_GCM,data:VNEV8a1KZc6XVeRzyBWzuwldTmxEepPRUOEMEM3HKrDIkxcGHDuoLh5P7Ti+jS5rbmua+ET4GPcJTYXR+pO5/cMaxqFONj1D1w9541QPYZNBbTfPM/Zfu8OnzngVsCnnKEtu1bVwflUnmf7F5hHED8zJRe1F9PT/HYA6NCd4ajQ=,iv:58ysTItP8UNnQWwgWRS1dk/K/2dJv3P5wa5rGnz2P/I=,tag:vLGrFldzOey9ANW010GylA==,type:str]
-smtp2go:
-    username: ENC[AES256_GCM,data:BEr7Rq7rlGvfYEpY/ZXnhM2eClnHdqU81A==,iv:dwYD5h+C5bzS9ikUgxQ51+jRQ32TtDy2PhDbd1tpS8Q=,tag:CjjLDz5n4H28qi8jWf9S4w==,type:str]
-    password: ENC[AES256_GCM,data:Yys6qy6DRYo16+X+Uj9oa9otjaKBnHOtIQ==,iv:G7H9mxsODShFoVlNMwuV8O18NBG/7LTFDFdqnH83YkE=,tag:hSlYp27QMoPZwiKBqyOpKA==,type:str]
 sops:
     kms: []
     gcp_kms: []
@@ -79,8 +53,8 @@ sops:
             azR0UkJyL0RwUVk4ZzdkSWptcDlWVjAK5FU9B5TBSnV3azO4eCv13T6i3dGGuI68
             UgBrVEb1/Fv+4XTjeSEhpiOaH8sNWYoNa3Aa7uTZYlHDRWga2GC7zw==
             -----END AGE ENCRYPTED FILE-----
-    lastmodified: "2025-05-26T20:32:01Z"
-    mac: ENC[AES256_GCM,data:si28Fu1crF2mYYCJAgN95+G8iJkn4T9wF0Itpi+5cjoSZ2ebxm2wWnVLQ9PwLIkHVF7nNbQM4fWy3eGIWWpexW6ReEc/aGJBLM0L4ho7iFaO1tzWEa5nTyz3QQH8kap1xvqEYgwH9EDkblc4gFpCUDnYbBt9lNcRCZ3JzeYoPxQ=,iv:QHsvuyCCn+9oe5ZQJi2/qDtV7Z2N4JMfqXUEqJkzKH4=,tag:2NGB4VR5bPEZmIC/lYX2VQ==,type:str]
+    lastmodified: "2024-12-01T13:22:41Z"
+    mac: ENC[AES256_GCM,data:6UqmxHJC4KWsiQttXFEEG1opPcrGntYj9nlD8m0iBqjc9g/SHxEogpaiYEnriGNXGw0HhRWjrd+JX29Ht4xVeiYqthYX+4rVuIuv+SI7p08hJeIBbIYrfonAJsebbSsynuy9YgyUkNZhoqjZTtuzFU/c4Dh5453RVnuQmu4PZNs=,iv:yA//mqJ0Ft63eRME8A1HBiZ/B0gcVYlS4MaP0LykooU=,tag:0NxU0lVi67N34eDhsT82kQ==,type:str]
     pgp: []
     unencrypted_suffix: _unencrypted
-    version: 3.9.4
+    version: 3.9.1