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