Compare commits

...
Sign in to create a new pull request.

51 commits

Author SHA1 Message Date
613bc83b89 Package Atuin as NixNG image 2025-02-19 17:44:14 +01:00
ae0d45e71f nextcloud: 30.0.5 -> 30.0.6
pihole: 2024.07.5 -> 2025.02.1
kitchenowl: 0.6.8 -> 0.6.10
hedgedoc: 1.10.0 -> 10.10.2
2025-02-19 13:52:36 +01:00
9f534327ab Update readme 2025-02-19 10:57:04 +01:00
a75fae6efb jellyfin: 10.10.5 -> 10.10.6 2025-02-18 22:10:04 +01:00
93ad02b2da Create Longhorn volume for music 2025-02-18 17:30:19 +01:00
851a7d0dcf Mount media locally 2025-02-17 22:43:34 +01:00
7418159761 Remove reliance on NFS volumes 2025-02-17 22:03:52 +01:00
f9541b865b little update on inbucket config 2025-02-16 15:53:31 +01:00
8cfa7d2b3d added config lines to Inbucket and bugjes opgelost in deployment scripts 2025-02-16 15:15:56 +01:00
201af045c9 Fix Traefik Helm values 2025-02-16 10:46:18 +01:00
028d7e781d Add Mealie service 2025-02-16 10:45:40 +01:00
268559dbce Add missing Longhorn backup target
Increase Jellyfin storage to 10Gi
Fix file system group for Ntfy files
2025-02-15 15:51:40 +01:00
d29332fd6d Enable OIDC login for kitchenowl 2025-02-13 17:59:35 +01:00
1210360509 Enable OIDC login into Nextcloud 2025-02-12 21:17:24 +01:00
ce635e415c Configure Authentik auth to Immich
Fix secret substituion for Authentik
2025-02-11 22:49:43 +01:00
63d30455a9 immich: 1.125.7 -> 1.126.1 2025-02-11 18:41:58 +01:00
5c9b23503a paperless-ngx: 2.14.5 -> 2.14.7 2025-02-11 18:36:53 +01:00
d1429d92a0 Enable OIDC login in Forgejo 2025-02-11 17:02:00 +01:00
d963610517 Enable Authentik auth on Paperless-ngx 2025-02-11 13:49:25 +01:00
81b553c8b0 Replace Authelia with Authentik 2025-02-10 22:51:18 +01:00
b09ce94621 Set Authelia consent mode for Hedgedoc to implicit
Add niels to hedgedoc group in Authelia
2025-02-09 15:35:51 +01:00
9838069c4c Enable Authelia auth for Hedgedoc 2025-02-09 00:23:12 +01:00
c69d909b2f Enable OIDC for FreshRSS on Tailscale 2025-02-08 22:09:40 +01:00
74c29e3fd0 forgejo: 10.0.0 -> 10.0.1 2025-02-08 15:26:08 +01:00
f97f7d4666 Share keepassxc using syncthing
Remove unused syncthing shares
2025-02-08 14:49:38 +01:00
ceebecbfa3 Fix consent mode for FreshRSS 2025-02-08 13:36:04 +01:00
7f1505878b Use OIDC auth for freshrss 2025-02-08 13:02:59 +01:00
05f020ecb3 authelia: enable 2fa
authelia: configure SMTP for notifications
2025-02-06 10:55:05 +01:00
b5fdd14ea6 Add persistent data for Authelia 2025-02-05 22:24:08 +01:00
20a72b00a6 Recreate and encrypt Authelia secrets 2025-02-05 18:06:30 +01:00
29ad11e6f2 MVP Authelia deployment 2025-02-04 17:24:51 +01:00
742f293a71 immich: 1.125.2 -> 1.125.7 2025-01-31 11:51:04 +01:00
ff5aa0526a prowlarr: 1.29.2.4915 -> 1.30.2.4939 2025-01-30 11:11:05 +01:00
58ba834eb0 jellyfin: 10.10.4 -> 10.10.5 2025-01-25 23:42:57 +01:00
e43d2a1475 Allow creating local GC roots for manifests 2025-01-25 23:42:41 +01:00
b2ffb5d1bb immich: 1.122.3 -> 1.125.2 2025-01-25 22:20:55 +01:00
36cf221454 atuin: 18.3.0 -> 18.4.0
paperless-ngx: 2.13.5 -> 2.14.5
nextcloud: 30.0.2 -> 30.0.5
syncthing: 1.28.0 -> 1.29.2
forgejo: 9.0.2 -> 10.0.0
kitchenowl: 0.6.4 -> 0.6.8
2025-01-22 17:31:10 +01:00
8fb2b3ab57 jellyfin: 10.10.3 -> 10.10.4 2025-01-22 17:16:51 +01:00
0c75d07f73 Add deployment options for media kubenix manifest
Cleanup sonarr log files
Increase size of sonarr volume
2025-01-18 16:20:24 +01:00
5b3687f802 Update flake inputs 2025-01-16 21:20:02 +01:00
7ca3ed9293 prowlarr: 1.28.2.4885 -> 1.29.2.4915 2025-01-12 00:06:36 +01:00
771c32b52c Use nixpkgs master branch for Bazarr and Radicale 2025-01-11 22:55:42 +01:00
8bc6313112 Format some files 2025-01-11 22:00:22 +01:00
b11f4bd67a radicale: 1.24.3 -> 1.25.0 2025-01-11 22:00:09 +01:00
9ae4ff3ca3 Fix GID of Jellyseerr
Run media containers with umask
2025-01-06 12:58:02 +01:00
abb7a131bc Package Deluge with NixNG
Use same group for all media images
2025-01-05 23:08:24 +01:00
a22c34716e Run all nixng containers under particular user/group 2025-01-05 00:17:35 +01:00
fe960448c6 Use group/user option for Radicale 2025-01-04 23:23:41 +01:00
e7ddf543c8 Use Jellyseerr from nixpkgs master branch 2025-01-04 11:54:21 +01:00
6106f2f795 Update flake inputs 2025-01-04 11:33:17 +01:00
3b0a54581d Move non-upstreamed NixNG modules into this repo 2025-01-04 11:22:05 +01:00
51 changed files with 1815 additions and 723 deletions

1
.gitignore vendored
View file

@ -1,3 +1,4 @@
.direnv .direnv
.pre-commit-config.yaml .pre-commit-config.yaml
result result
.manifests

110
README.md
View file

@ -5,74 +5,84 @@ We use [Kubenix](https://kubenix.org/) to write Kubernetes deployments in Nix!
## Images used ## Images used
Legend: 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 - ✅: Official image or trusted publisher
- 🫤: Unofficial image - 🫤: Unofficial image
| Status | Image | Comments | | Status | Image | Comments |
| --- | --- | --- | | ------ | ---------------------------------------------- | --------------------------------------------------------- |
| ✨ | `nixng-blog` | | | ✨ | `nixng-blog` | |
| ✨ | `nixng-dnsmasq` | | | ✨ | `nixng-dnsmasq` | |
| ✨ | `nixng-attic` | | | ✨ | `nixng-attic` | |
| ✨ | `nixng-ntfy-sh` | | | ✨ | `nixng-ntfy-sh` | |
| ✨ | `nixng-radicale` | | | ✨ | `nixng-radicale` | |
| ✨ | `nixng-jellyseerr` | | | ✨ | `nixng-jellyseerr` | |
| ✨ | `nixng-radarr` | | | ✨ | `nixng-radarr` | |
| ✨ | `nixng-sonarr` | | | ✨ | `nixng-sonarr` | |
| ✨ | `nixng-bazarr` | | | ✨ | `nixng-bazarr` | |
| ✨ | `nixng-prowlarr` | | | ✨ | `nixng-prowlarr` | |
| ✅ | `jellyfin/jellyfin` | | | ✨ | `nixng-deluge` | |
| ✅ | `linuxserver/deluge` | | | ✨ | `nixng-mealie` | |
| ✅ | `ghcr.io/atuinsh/atuin` | | | ✨ | `nixng-atuin` | |
| ✅ | `postgres:14` | Database for Atuin | | ✅ | `jellyfin/jellyfin` | |
| ✅ | `ghcr.io/paperless-ngx/paperless-ngx` | | | ✅ | `postgres:14` | Database for Atuin |
| ✅ | `docker.io/library/redis:7` | Database for Paperless-ngx | | ✅ | `ghcr.io/paperless-ngx/paperless-ngx` | |
| ✅ | `nextcloud` | | | ✅ | `docker.io/library/redis:7` | Database for Paperless-ngx |
| ✅ | `postgres:15` | Database for Attic, Nextcloud, Paperless-ngx and Hedgedoc | | ✅ | `nextcloud` | |
| ✅ | `inbucket/inbucket` | | | ✅ | `postgres:15` | Database for Attic, Nextcloud, Paperless-ngx and Hedgedoc |
| ✅ | `lscr.io/linuxserver/syncthing` | | | ✅ | `inbucket/inbucket` | |
| ✅ | `codeberg.org/forgejo/forgejo` | | | ✅ | `lscr.io/linuxserver/syncthing` | |
| ✅ | `pihole/pihole` | | | ✅ | `codeberg.org/forgejo/forgejo` | |
| ✅ | `ghcr.io/immich-app/immich-server` | | | ✅ | `pihole/pihole` | |
| ✅ | `ghcr.io/immich-app/immich-machine-learning` | | | ✅ | `ghcr.io/immich-app/immich-server` | |
| ✅ | `docker.io/redis:6.2-alpine` | Database for Immich | | ✅ | `ghcr.io/immich-app/immich-machine-learning` | |
| ✅ | `docker.io/tensorchord/pgvecto-rs:pg14-v0.2.0` | Database for Immich | | ✅ | `docker.io/redis:6.2-alpine` | Database for Immich |
| ✅ | `tombursch/kitchenowl` | | | ✅ | `docker.io/tensorchord/pgvecto-rs:pg14-v0.2.0` | Database for Immich |
| ✅ | `freshrss/freshrss` | | | ✅ | `tombursch/kitchenowl` | |
| ✅ | `ubuntu/bind9` | | | ✅ | `freshrss/freshrss` | |
| ✅ | `quay.io/hedgedoc/hedgedoc` | | | ✅ | `ubuntu/bind9` | |
| 🫤 | `itzg/minecraft-server` | | | ✅ | `quay.io/hedgedoc/hedgedoc` | |
| 🫤 | `teddysun/kms` | | | 🫤 | `itzg/minecraft-server` | |
| 🫤 | `mpepping/cyberchef` | | | 🫤 | `teddysun/kms` | |
| 🫤 | `mpepping/cyberchef` | |
## Acknowledgements ## Acknowledgements
- [dns.nix](https://github.com/kirelagin/dns.nix): A Nix DSL for defining DNS zones - [dns.nix](https://github.com/kirelagin/dns.nix): A Nix DSL for defining DNS
- [flake-utils](https://github.com/numtide/flake-utils): Handy utilities to develop Nix flakes zones
- [kubenix](https://kubenix.org/): Declare and deploy Kubernetes resources using Nix - [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 - [nixhelm](https://github.com/farcaller/nixhelm): Nix-digestible Helm charts
- [sops-nix](https://github.com/Mic92/sops-nix): Sops secret management for Nix - [sops-nix](https://github.com/Mic92/sops-nix): Sops secret management for Nix
## Prerequisites ## Prerequisites
To deploy to the Kubernetes cluster, first make sure you have an admin account on the cluster. To deploy to the Kubernetes cluster, first make sure you have an admin account
You can generate this using `nix run '.#gen-k3s-cert' <username> <servername> ~/.kube`, assuming you have SSH access to the master node. on the cluster. You can generate this using
This puts a private key, signed certificate and a kubeconfig in the kubeconfig directory `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 ## Bootstrapping
We are now ready to deploy to the Kubernetes cluster. We are now ready to deploy to the Kubernetes cluster. Deployments are done
Deployments are done through an experimental Kubernetes feature called [ApplySets](https://kubernetes.io/docs/tasks/manage-kubernetes-objects/declarative-config/#how-to-delete-objects). 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. 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. If the cluster has not been initialized yet, we must bootstrap it first. Run
Run these deployments: these deployments:
- `nix run '.#bootstrap-default-deploy'` - `nix run '.#bootstrap-default-deploy'`
- `nix run '.#bootstrap-kube-system-deploy'` - `nix run '.#bootstrap-kube-system-deploy'`
## Deployment ## Deployment
Now the cluster has been initialized and we can deploy applications. Now the cluster has been initialized and we can deploy applications. To explore
To explore which applications we can deploy, run `nix flake show`. which applications we can deploy, run `nix flake show`. Then, for each
Then, for each application, run `nix run '.#<application>-deploy'`. application, run `nix run '.#<application>-deploy'`. Or, if you're lazy:
Or, if you're lazy: `nix flake show --json | jq -r '.packages."x86_64-linux"|keys[]' | grep -- -deploy | xargs -I{} nix run ".#{}"`. `nix flake show --json | jq -r '.packages."x86_64-linux"|keys[]' | grep -- -deploy | xargs -I{} nix run ".#{}"`.

View file

@ -2,11 +2,31 @@
set -euo pipefail 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%% *}" first_server="${SERVERS%% *}"
previous_manifest=$(ssh -T "root@$first_server" << EOF previous_manifest=$(
if [[ -f "$GCROOTDIR/${NAME}.yml" ]]; then envsubst <<EOF | ssh -T "root@$first_server.dmz"
cat "$GCROOTDIR/${NAME}.yml" if [[ -f "$GCROOTDIR/$NAME.yml" ]]; then
fi cat "$GCROOTDIR/$NAME.yml"
fi
EOF EOF
) )
@ -14,30 +34,35 @@ set +e
if [ -z "$previous_manifest" ]; then if [ -z "$previous_manifest" ]; then
echo No previous manifest found! echo No previous manifest found!
else else
$DYFF between <(echo $previous_manifest) $MANIFEST \ $DYFF between <(echo "$previous_manifest") "$MANIFEST" \
--exclude-regexp metadata.labels.kubenix/hash \ --exclude-regexp metadata.labels.kubenix/hash \
--exclude-regexp labels.kubenix/hash \ --exclude-regexp labels.kubenix/hash \
--set-exit-code --set-exit-code
if [ $? -eq 0 ]; then
exit 0
fi
fi fi
set -e set -e
read -r -p "Continue? " response read -r -p "Continue? " _
echo Uploading closure... echo Uploading closure...
for server in $SERVERS; do for server in $SERVERS; do
echo Uploading closure to $server... echo Uploading closure to "$server"...
nix copy --to "ssh://root@$server.dmz" $MANIFEST nix copy --to "ssh://root@$server.dmz" "$MANIFEST"
ssh "root@$server.dmz" "mkdir -p $GCROOTDIR && ln -sf $MANIFEST $GCROOTDIR/${NAME}.yml"
done done
echo Applying Kubernetes manifest... echo Applying Kubernetes manifest...
export KUBECTL_APPLYSET=true export KUBECTL_APPLYSET=true
vals eval -fail-on-missing-key-in-map <$MANIFEST | \ vals eval -fail-on-missing-key-in-map <"$MANIFEST" |
kubectl apply -f - \ kubectl apply -f - \
--prune \ --prune \
--applyset applyset-$NAME \ --applyset applyset-"$NAME" \
--namespace $NAMESPACE --namespace "$NAMESPACE"
echo Creating GC roots
for server in $SERVERS; do
ssh "root@$server.dmz" "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

View file

@ -123,4 +123,14 @@
module.ntfy.enable = true; module.ntfy.enable = true;
namespace = "ntfy"; namespace = "ntfy";
}; };
authentik = {
module.authentik.enable = true;
namespace = "authentik";
};
mealie = {
module.mealie.enable = true;
namespace = "mealie";
};
} }

134
flake.lock generated
View file

@ -6,11 +6,11 @@
"nixpkgs": "nixpkgs" "nixpkgs": "nixpkgs"
}, },
"locked": { "locked": {
"lastModified": 1735508994, "lastModified": 1736683360,
"narHash": "sha256-SMMX3irZ4Y+0QEAq0mOYEnJIYRe3YnXHrkCSRvdxHxU=", "narHash": "sha256-Zz9aEPm5TCtoNt+FoWQaEjngEh8uXDpCH+N2DDpW4Tk=",
"ref": "refs/heads/master", "ref": "refs/heads/master",
"rev": "433c1ef4b5874e2c4782be7322604d17182035ab", "rev": "95b66d8c45b1fda87e63938fbdc0e31d5035f204",
"revCount": 23, "revCount": 24,
"type": "git", "type": "git",
"url": "https://git.kun.is/pim/blog" "url": "https://git.kun.is/pim/blog"
}, },
@ -383,15 +383,14 @@
"gitignore": "gitignore", "gitignore": "gitignore",
"nixpkgs": [ "nixpkgs": [
"nixpkgs" "nixpkgs"
], ]
"nixpkgs-stable": "nixpkgs-stable"
}, },
"locked": { "locked": {
"lastModified": 1734797603, "lastModified": 1735882644,
"narHash": "sha256-ulZN7ps8nBV31SE+dwkDvKIzvN6hroRY8sYOT0w+E28=", "narHash": "sha256-3FZAG+pGt3OElQjesCAWeMkQ7C/nB1oTHLRQ8ceP110=",
"owner": "cachix", "owner": "cachix",
"repo": "git-hooks.nix", "repo": "git-hooks.nix",
"rev": "f0f0dc4920a903c3e08f5bdb9246bb572fcae498", "rev": "a5a961387e75ae44cc20f0a57ae463da5e959656",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -408,7 +407,7 @@
"servers", "servers",
"nixpkgs-unstable" "nixpkgs-unstable"
], ],
"nixpkgs-stable": "nixpkgs-stable_2" "nixpkgs-stable": "nixpkgs-stable"
}, },
"locked": { "locked": {
"lastModified": 1730302582, "lastModified": 1730302582,
@ -560,11 +559,11 @@
"nginx": { "nginx": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1735301654, "lastModified": 1736428764,
"narHash": "sha256-PHcSyHYyPUwPAls0BgtnGu2e936vhxW2nt7bQxDyGAQ=", "narHash": "sha256-poKKXq1S4xjC9phulyZE34t8tdaaqwJ7IbmeyjUpsDU=",
"owner": "nginx", "owner": "nginx",
"repo": "nginx", "repo": "nginx",
"rev": "e3a9b6ad08a86e799a3d77da3f2fc507d3c9699e", "rev": "57d54fd922e7ecbebb78598d13adc9df1a4b69c0",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -667,11 +666,11 @@
"poetry2nix": "poetry2nix" "poetry2nix": "poetry2nix"
}, },
"locked": { "locked": {
"lastModified": 1735607967, "lastModified": 1739200411,
"narHash": "sha256-hdOdhQskvxyPPrf4w/k484xfVEVsqktHjwS0noTRRCw=", "narHash": "sha256-9Vil9l0+QIPhEh/97Ehu3yoqaR+5d820F/tMY6rtbYs=",
"owner": "farcaller", "owner": "farcaller",
"repo": "nixhelm", "repo": "nixhelm",
"rev": "6ce9cfd0e06bbf609af333069b3c4e84cd739755", "rev": "5b365cdeae7077e6c06524d5317f82a593546b50",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -688,16 +687,16 @@
"treefmt-nix": "treefmt-nix_2" "treefmt-nix": "treefmt-nix_2"
}, },
"locked": { "locked": {
"lastModified": 1735551011, "lastModified": 1736029062,
"narHash": "sha256-rp26PcdLjfgxsCeaSeZ0K1rGPN1Ap7YCy1UVuo9gRJA=", "narHash": "sha256-7X65+TP0luFpQsA6KV80R05qnWp7NxMaIDryFfJ4MqI=",
"owner": "pizzapim", "owner": "pizzapim",
"repo": "NixNG", "repo": "NixNG",
"rev": "aa35f7a3d426e906b15e3083c90bf2972bcfb4b4", "rev": "6211c11d7ef2cc8067efcd169e0b8fd02f1816b6",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "pizzapim", "owner": "pizzapim",
"ref": "kubernetes", "ref": "dinit-fixes",
"repo": "NixNG", "repo": "NixNG",
"type": "github" "type": "github"
} }
@ -771,45 +770,13 @@
"type": "github" "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": { "nixpkgs-master": {
"locked": { "locked": {
"lastModified": 1735935963, "lastModified": 1737014460,
"narHash": "sha256-i6xTJb3sb4BeWypD/DjAmslDzGXZUGU1OFJliaKFuuc=", "narHash": "sha256-u45ycukf/qnwb3EsOHf5KuO7GPxR1noxgkiL5Fra2V4=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "20166d17f391d2e11311baaa74344381fa44e4a0", "rev": "d6a640c0d7d42202fdabc93bbfc01430af249e0c",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -819,39 +786,7 @@
"type": "github" "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": { "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": { "locked": {
"lastModified": 1720386169, "lastModified": 1720386169,
"narHash": "sha256-NGKVY4PjzwAa4upkGtAMz1npHGoRzWotlSnVlqI40mo=", "narHash": "sha256-NGKVY4PjzwAa4upkGtAMz1npHGoRzWotlSnVlqI40mo=",
@ -867,7 +802,7 @@
"type": "github" "type": "github"
} }
}, },
"nixpkgs-stable_3": { "nixpkgs-stable_2": {
"locked": { "locked": {
"lastModified": 1729357638, "lastModified": 1729357638,
"narHash": "sha256-66RHecx+zohbZwJVEPF7uuwHeqf8rykZTMCTqIrOew4=", "narHash": "sha256-66RHecx+zohbZwJVEPF7uuwHeqf8rykZTMCTqIrOew4=",
@ -917,11 +852,11 @@
}, },
"nixpkgs_3": { "nixpkgs_3": {
"locked": { "locked": {
"lastModified": 1735471104, "lastModified": 1736883708,
"narHash": "sha256-0q9NGQySwDQc7RhAV2ukfnu7Gxa5/ybJ2ANT8DQrQrs=", "narHash": "sha256-uQ+NQ0/xYU0N1CnXsa2zghgNaOPxWpMJXSUJJ9W7140=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "88195a94f390381c6afcdaa933c2f6ff93959cb4", "rev": "eb62e6aa39ea67e0b8018ba8ea077efe65807dc8",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -965,11 +900,11 @@
}, },
"nixpkgs_6": { "nixpkgs_6": {
"locked": { "locked": {
"lastModified": 1733097829, "lastModified": 1735554305,
"narHash": "sha256-9hbb1rqGelllb4kVUCZ307G2k3/UhmA8PPGBoyuWaSw=", "narHash": "sha256-zExSA1i/b+1NMRhGGLtNfFGXgLtgo+dcuzHzaWA6w3Q=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "2c15aa59df0017ca140d9ba302412298ab4bf22a", "rev": "0e82ab234249d8eee3e8c91437802b32c74bb3fd",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -1016,10 +951,7 @@
"nixhelm": "nixhelm", "nixhelm": "nixhelm",
"nixng": "nixng", "nixng": "nixng",
"nixpkgs": "nixpkgs_3", "nixpkgs": "nixpkgs_3",
"nixpkgs-bazarr": "nixpkgs-bazarr",
"nixpkgs-jellyseerr": "nixpkgs-jellyseerr",
"nixpkgs-master": "nixpkgs-master", "nixpkgs-master": "nixpkgs-master",
"nixpkgs-radicale": "nixpkgs-radicale",
"servers": "servers", "servers": "servers",
"treefmt-nix": "treefmt-nix_4" "treefmt-nix": "treefmt-nix_4"
} }
@ -1063,7 +995,7 @@
"servers", "servers",
"nixpkgs" "nixpkgs"
], ],
"nixpkgs-stable": "nixpkgs-stable_3" "nixpkgs-stable": "nixpkgs-stable_2"
}, },
"locked": { "locked": {
"lastModified": 1729775275, "lastModified": 1729775275,
@ -1295,11 +1227,11 @@
"nixpkgs": "nixpkgs_6" "nixpkgs": "nixpkgs_6"
}, },
"locked": { "locked": {
"lastModified": 1735649548, "lastModified": 1736154270,
"narHash": "sha256-/4pTzlmABhx26AOTYvFN1OTCxJJL/LBUB49giqoMhJA=", "narHash": "sha256-p2r8xhQZ3TYIEKBoiEhllKWQqWNJNoT9v64Vmg4q8Zw=",
"owner": "numtide", "owner": "numtide",
"repo": "treefmt-nix", "repo": "treefmt-nix",
"rev": "597705118f16d1dcd0fef99707700d13b2b324d7", "rev": "13c913f5deb3a5c08bb810efd89dc8cb24dd968b",
"type": "github" "type": "github"
}, },
"original": { "original": {

View file

@ -7,9 +7,6 @@
flake-utils.url = "github:numtide/flake-utils"; flake-utils.url = "github:numtide/flake-utils";
treefmt-nix.url = "github:numtide/treefmt-nix"; treefmt-nix.url = "github:numtide/treefmt-nix";
blog.url = "git+https://git.kun.is/pim/blog"; 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 = { git-hooks = {
url = "github:cachix/git-hooks.nix"; url = "github:cachix/git-hooks.nix";
@ -37,7 +34,7 @@
}; };
nixng = { nixng = {
url = "github:pizzapim/NixNG/kubernetes"; url = "github:pizzapim/NixNG/dinit-fixes";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
}; };
@ -60,5 +57,6 @@
./formatter.nix ./formatter.nix
./shell.nix ./shell.nix
./nixng-configurations ./nixng-configurations
./nixng-modules
]; ];
} }

View file

@ -1,28 +1,27 @@
{servers, ...}: let {servers, ...}: let
globals = { globals = {
images = { images = {
jellyfin = "jellyfin/jellyfin:10.10.3"; jellyfin = "jellyfin/jellyfin:10.10.6";
deluge = "linuxserver/deluge:2.1.1"; atuin = "ghcr.io/atuinsh/atuin:18.4.0";
atuin = "ghcr.io/atuinsh/atuin:18.3.0";
postgres14 = "postgres:14"; postgres14 = "postgres:14";
kms = "teddysun/kms:latest"; kms = "teddysun/kms:latest";
paperless = "ghcr.io/paperless-ngx/paperless-ngx:2.13.5"; paperless = "ghcr.io/paperless-ngx/paperless-ngx:2.14.7";
redis7 = "docker.io/library/redis:7"; redis7 = "docker.io/library/redis:7";
nextcloud = "nextcloud:30.0.2"; nextcloud = "nextcloud:30.0.6";
postgres15 = "postgres:15"; postgres15 = "postgres:15";
inbucket = "inbucket/inbucket:edge"; inbucket = "inbucket/inbucket:edge";
syncthing = "lscr.io/linuxserver/syncthing:1.28.0"; syncthing = "lscr.io/linuxserver/syncthing:1.29.2";
forgejo = "codeberg.org/forgejo/forgejo:9.0.2"; forgejo = "codeberg.org/forgejo/forgejo:10.0.1";
pihole = "pihole/pihole:2024.07.0"; pihole = "pihole/pihole:2025.02.1";
immich = "ghcr.io/immich-app/immich-server:v1.122.3"; immich = "ghcr.io/immich-app/immich-server:v1.126.1";
immich-machine-learning = "ghcr.io/immich-app/immich-machine-learning:v1.122.3"; immich-machine-learning = "ghcr.io/immich-app/immich-machine-learning:v1.126.1";
immich-redis = "docker.io/redis:6.2-alpine@sha256:eaba718fecd1196d88533de7ba49bf903ad33664a92debb24660a922ecd9cac8"; immich-redis = "docker.io/redis:6.2-alpine@sha256:905c4ee67b8e0aa955331960d2aa745781e6bd89afc44a8584bfd13bc890f0ae";
immich-postgres = "docker.io/tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:90724186f0a3517cf6914295b5ab410db9ce23190a2d9d0b9dd6463e3fa298f0"; immich-postgres = "docker.io/tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:90724186f0a3517cf6914295b5ab410db9ce23190a2d9d0b9dd6463e3fa298f0";
kitchenowl = "tombursch/kitchenowl:v0.6.4"; kitchenowl = "tombursch/kitchenowl:v0.6.10";
cyberchef = "mpepping/cyberchef:latest"; cyberchef = "mpepping/cyberchef:latest";
freshrss = "freshrss/freshrss:1.24.3"; freshrss = "freshrss/freshrss:1.25.0";
bind9 = "ubuntu/bind9:9.18-22.04_beta"; bind9 = "ubuntu/bind9:9.18-22.04_beta";
hedgedoc = "quay.io/hedgedoc/hedgedoc:1.10.0"; hedgedoc = "quay.io/hedgedoc/hedgedoc:1.10.2";
minecraft = "itzg/minecraft-server:latest"; minecraft = "itzg/minecraft-server:latest";
}; };

View file

@ -8,7 +8,7 @@ inputs @ {
flake-utils.lib.eachDefaultSystem flake-utils.lib.eachDefaultSystem
(system: let (system: let
pkgs = nixpkgs.legacyPackages.${system}; pkgs = nixpkgs.legacyPackages.${system};
lib = pkgs.lib; inherit (pkgs) lib;
deployScript = (pkgs.writeScriptBin "applyset-deploy.sh" (builtins.readFile ./applyset-deploy.sh)).overrideAttrs (old: { deployScript = (pkgs.writeScriptBin "applyset-deploy.sh" (builtins.readFile ./applyset-deploy.sh)).overrideAttrs (old: {
buildCommand = "${old.buildCommand}\npatchShebangs $out"; buildCommand = "${old.buildCommand}\npatchShebangs $out";
}); });
@ -64,7 +64,7 @@ flake-utils.lib.eachDefaultSystem
pkgs.symlinkJoin pkgs.symlinkJoin
{ {
name = "applyset-deploy.sh"; name = "applyset-deploy.sh";
paths = [deployScript pkgs.vals pkgs.kubectl]; paths = [deployScript pkgs.vals pkgs.kubectl pkgs.gettext];
buildInputs = [pkgs.makeWrapper]; buildInputs = [pkgs.makeWrapper];
passthru.manifest = result; passthru.manifest = result;
meta.mainProgram = "applyset-deploy.sh"; meta.mainProgram = "applyset-deploy.sh";

View file

@ -1,5 +1,4 @@
{ {
self,
utils, utils,
lib, lib,
config, config,

View file

@ -1,5 +1,6 @@
{ {
config, config,
utils,
globals, globals,
lib, lib,
... ...
@ -29,35 +30,17 @@
metadata.labels.app = "atuin"; metadata.labels.app = "atuin";
spec = { spec = {
volumes = { volumes.database.persistentVolumeClaim.claimName = "database";
data.persistentVolumeClaim.claimName = "data";
database.persistentVolumeClaim.claimName = "database";
};
containers = { containers = {
atuin = { atuin = {
image = globals.images.atuin; image = utils.mkNixNGImage "atuin";
imagePullPolicy = "IfNotPresent";
ports.web.containerPort = 8888; ports.web.containerPort = 8888;
args = ["server" "start"];
env = { env.ATUIN_DB_URI.valueFrom.secretKeyRef = {
ATUIN_HOST.value = "0.0.0.0"; name = "database";
ATUIN_PORT.value = "8888"; key = "databaseURL";
ATUIN_OPEN_REGISTRATION.value = "false";
ATUIN_DB_URI.valueFrom.secretKeyRef = {
name = "database";
key = "databaseURL";
};
}; };
volumeMounts = [
{
name = "data";
mountPath = "/config";
}
];
}; };
database = { database = {
@ -106,16 +89,9 @@
}; };
}; };
longhorn.persistentVolumeClaim = { longhorn.persistentVolumeClaim.database = {
data = { volumeName = "atuin-db";
volumeName = "atuin"; storage = "300Mi";
storage = "300Mi";
};
database = {
volumeName = "atuin-db";
storage = "300Mi";
};
}; };
}; };
}; };

92
modules/authentik.nix Normal file
View file

@ -0,0 +1,92 @@
{
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.existingClaim = "db";
primary.extraEnvVarsSecret = "postgresql-env";
};
redis = {
enabled = true;
master.persistence.existingClaim = "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;
};
};
};
lab = {
longhorn.persistentVolumeClaim = {
db = {
volumeName = "authentik-db";
storage = "10Gi";
};
redis = {
volumeName = "authentik-redis";
storage = "5Gi";
};
};
ingresses.authentik = {
host = "authentik.kun.is";
service = {
name = "authentik-server";
portName = "http";
};
};
tailscaleIngresses = {
tailscale-authentik = {
host = "authentik";
service = {
name = "authentik-server";
portName = "http";
};
};
};
};
};
}

View file

@ -16,11 +16,6 @@
includeCRDs = true; includeCRDs = true;
}; };
# argo-workflows = {
# chart = nixhelm.chartsDerivations.${system}.argoproj.argo-workflows;
# includeCRDs = true;
# };
longhorn = { longhorn = {
chart = nixhelm.chartsDerivations.${system}.longhorn.longhorn; chart = nixhelm.chartsDerivations.${system}.longhorn.longhorn;
includeCRDs = true; includeCRDs = true;
@ -62,11 +57,13 @@
minecraft = {}; minecraft = {};
tailscale = {}; tailscale = {};
ntfy = {}; ntfy = {};
authentik = {};
mealie = {};
}; };
nodes = nodes =
builtins.mapAttrs builtins.mapAttrs
(name: labels: { (_name: labels: {
metadata.labels = labels; metadata.labels = labels;
}) })
globals.nodeLabels; globals.nodeLabels;
@ -78,30 +75,16 @@
concurrency = 1; concurrency = 1;
}; };
backuptargets.backup.spec = {
backupTargetURL = "nfs://lewis.dmz:/mnt/longhorn/persistent/longhorn-backup";
pollInterval = "5m0s";
};
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"]; 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 = {}; l2Advertisements.main.metadata = {};
persistentVolumes = { # We don't need backups for music, just replication is enough.
music-syncthing.spec = { persistentVolumes.music.spec.csi.volumeAttributes.recurringJobSelector = lib.mkForce "";
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";
};
};
};
}; };
}; };
@ -109,7 +92,6 @@
longhorn.persistentVolume = { longhorn.persistentVolume = {
freshrss.storage = "1Gi"; freshrss.storage = "1Gi";
radicale.storage = "200Mi"; radicale.storage = "200Mi";
atuin.storage = "300Mi";
atuin-db.storage = "300Mi"; atuin-db.storage = "300Mi";
nextcloud.storage = "50Gi"; nextcloud.storage = "50Gi";
nextcloud-db.storage = "400Mi"; nextcloud-db.storage = "400Mi";
@ -127,16 +109,21 @@
immich-db.storage = "5Gi"; immich-db.storage = "5Gi";
attic.storage = "15Gi"; attic.storage = "15Gi";
attic-db.storage = "150Mi"; attic-db.storage = "150Mi";
jellyfin.storage = "5Gi"; jellyfin.storage = "10Gi";
transmission.storage = "25Mi"; transmission.storage = "25Mi";
jellyseerr.storage = "75Mi"; jellyseerr.storage = "75Mi";
radarr.storage = "300Mi"; radarr.storage = "300Mi";
prowlarr.storage = "150Mi"; prowlarr.storage = "150Mi";
sonarr.storage = "150Mi"; sonarr.storage = "250Mi";
bazarr.storage = "25Mi"; bazarr.storage = "25Mi";
minecraft.storage = "1Gi"; minecraft.storage = "1Gi";
ntfy.storage = "300Mi"; ntfy.storage = "300Mi";
deluge.storage = "500Mi"; deluge.storage = "500Mi";
keepassxc.storage = "100Mi";
authentik-db.storage = "10Gi";
authentik-redis.storage = "5Gi";
mealie.storage = "3Gi";
music.storage = "70Gi";
}; };
tailscaleIngresses.tailscale-longhorn = { tailscaleIngresses.tailscale-longhorn = {

View file

@ -29,5 +29,7 @@
./tailscale.nix ./tailscale.nix
./ntfy.nix ./ntfy.nix
./minecraft.nix ./minecraft.nix
./authentik.nix
./mealie.nix
]; ];
} }

View file

@ -38,5 +38,19 @@
version = "v1beta1"; version = "v1beta1";
kind = "RecurringJob"; kind = "RecurringJob";
}; };
middlewares = {
attrName = "middlewares";
group = "traefik.io";
version = "v1alpha1";
kind = "Middleware";
};
backuptargets = {
attrName = "backuptargets";
group = "longhorn.io";
version = "v1beta1";
kind = "BackupTarget";
};
}; };
} }

View file

@ -7,11 +7,18 @@
"repository.pull-request".DEFAULT_MERGE_STYLE = "merge"; "repository.pull-request".DEFAULT_MERGE_STYLE = "merge";
"repository.signing".DEFAULT_TRUST_MODEL = "committer"; "repository.signing".DEFAULT_TRUST_MODEL = "committer";
ui.DEFAULT_THEME = "forgejo-light"; ui.DEFAULT_THEME = "forgejo-light";
oauth2 = { oauth2 = {
ENABLED = false; ENABLED = true;
JWT_SECRET = "ref+sops://secrets.yml#/forgejo/jwtSecret"; JWT_SECRET = "ref+sops://secrets.yml#/forgejo/jwtSecret";
}; };
oauth2_client = {
ENABLE_AUTO_REGISTRATION = true;
ACCOUNT_LINKING = "auto";
USERNAME = "email";
};
DEFAULT = { DEFAULT = {
APP_NAME = "Forgejo: Beyond coding. We forge."; APP_NAME = "Forgejo: Beyond coding. We forge.";
RUN_MODE = "prod"; RUN_MODE = "prod";
@ -85,11 +92,11 @@
}; };
service = { service = {
DISABLE_REGISTRATION = true; DISABLE_REGISTRATION = false;
REQUIRE_SIGNIN_VIEW = false; REQUIRE_SIGNIN_VIEW = false;
REGISTER_EMAIL_CONFIRM = false; REGISTER_EMAIL_CONFIRM = false;
ENABLE_NOTIFY_MAIL = false; ENABLE_NOTIFY_MAIL = false;
ALLOW_ONLY_EXTERNAL_REGISTRATION = false; ALLOW_ONLY_EXTERNAL_REGISTRATION = true;
ENABLE_CAPTCHA = false; ENABLE_CAPTCHA = false;
DEFAULT_KEEP_EMAIL_PRIVATE = true; DEFAULT_KEEP_EMAIL_PRIVATE = true;
DEFAULT_ALLOW_CREATE_ORGANIZATION = true; DEFAULT_ALLOW_CREATE_ORGANIZATION = true;
@ -98,7 +105,7 @@
}; };
openid = { openid = {
ENABLE_OPENID_SIGNIN = true; ENABLE_OPENID_SIGNIN = false;
ENABLE_OPENID_SIGNUP = false; ENABLE_OPENID_SIGNUP = false;
}; };
} }

View file

@ -36,6 +36,13 @@
CRON_MIN.value = "2,32"; CRON_MIN.value = "2,32";
ADMIN_EMAIL.value = "pim@kunis.nl"; ADMIN_EMAIL.value = "pim@kunis.nl";
PUBLISHED_PORT.value = "443"; 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 = { ADMIN_PASSWORD.valueFrom.secretKeyRef = {
name = "server"; name = "server";

View file

@ -54,6 +54,17 @@
CMD_PROTOCOL_USESSL.value = "true"; CMD_PROTOCOL_USESSL.value = "true";
CMD_CSP_ENABLE.value = "false"; 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 = { CMD_DB_URL.valueFrom.secretKeyRef = {
name = "hedgedoc"; name = "hedgedoc";
key = "databaseURL"; key = "databaseURL";

View file

@ -21,7 +21,14 @@
containers.inbucket = { containers.inbucket = {
image = globals.images.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 = { ports = {
web.containerPort = 9000; web.containerPort = 9000;
smtp.containerPort = 2500; smtp.containerPort = 2500;

View file

@ -47,7 +47,7 @@ in {
rules = [ rules = [
{ {
host = ingress.host; inherit (ingress) host;
http.paths = [ http.paths = [
{ {
@ -55,7 +55,7 @@ in {
pathType = "Prefix"; pathType = "Prefix";
backend.service = { backend.service = {
name = ingress.service.name; inherit (ingress.service) name;
port.name = ingress.service.portName; port.name = ingress.service.portName;
}; };
} }

View file

@ -33,9 +33,16 @@
ports.web.containerPort = 8080; ports.web.containerPort = 8080;
imagePullPolicy = "IfNotPresent"; imagePullPolicy = "IfNotPresent";
env.JWT_SECRET_KEY.valueFrom.secretKeyRef = { env = {
name = "server"; FRONT_URL.value = "https://boodschappen.kun.is";
key = "jwtSecretKey"; 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";
};
}; };
volumeMounts = [ volumeMounts = [

View file

@ -3,7 +3,7 @@
config, config,
... ...
}: let }: let
longhornVolumeOpts = {name, ...}: { longhornVolumeOpts = _: {
options = { options = {
storage = lib.mkOption { storage = lib.mkOption {
type = lib.types.str; type = lib.types.str;
@ -16,7 +16,7 @@
}; };
}; };
longhornPVOpts = {name, ...}: { longhornPVOpts = _: {
options = { options = {
storage = lib.mkOption { storage = lib.mkOption {
type = lib.types.str; type = lib.types.str;
@ -71,7 +71,7 @@ in {
claimRef = { claimRef = {
inherit name; inherit name;
namespace = longhornVolume.namespace; inherit (longhornVolume) namespace;
}; };
csi = { csi = {
@ -134,7 +134,7 @@ in {
persistentVolumeClaims = persistentVolumeClaims =
lib.mergeAttrs lib.mergeAttrs
(builtins.mapAttrs (builtins.mapAttrs
(name: longhornVolume: { (_name: longhornVolume: {
spec = { spec = {
accessModes = ["ReadWriteOnce"]; accessModes = ["ReadWriteOnce"];
resources.requests.storage = longhornVolume.storage; resources.requests.storage = longhornVolume.storage;
@ -143,12 +143,12 @@ in {
}) })
config.lab.longhornVolumes) config.lab.longhornVolumes)
(builtins.mapAttrs (builtins.mapAttrs
(name: longhornPVC: { (_name: longhornPVC: {
spec = { spec = {
accessModes = ["ReadWriteOnce"]; accessModes = ["ReadWriteOnce"];
resources.requests.storage = longhornPVC.storage; resources.requests.storage = longhornPVC.storage;
storageClassName = ""; storageClassName = "";
volumeName = longhornPVC.volumeName; inherit (longhornPVC) volumeName;
}; };
}) })
config.lab.longhorn.persistentVolumeClaim); config.lab.longhorn.persistentVolumeClaim);

76
modules/mealie.nix Normal file
View file

@ -0,0 +1,76 @@
{
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 = {
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.persistentVolumeClaim.claimName = "mealie";
};
};
};
services.mealie.spec = {
selector.app = "mealie";
ports.web = {
port = 80;
targetPort = "web";
};
};
};
lab = {
ingresses.mealie = {
host = "mealie.kun.is";
service = {
name = "mealie";
portName = "web";
};
};
longhorn.persistentVolumeClaim.mealie = {
volumeName = "mealie";
storage = "3Gi";
};
};
};
}

File diff suppressed because it is too large Load diff

View file

@ -50,6 +50,11 @@
attachment-cache.persistentVolumeClaim.claimName = "attachment-cache"; attachment-cache.persistentVolumeClaim.claimName = "attachment-cache";
data.persistentVolumeClaim.claimName = "data"; data.persistentVolumeClaim.claimName = "data";
}; };
securityContext = {
fsGroup = 407;
fsGroupChangePolicy = "Always";
};
}; };
}; };
}; };

View file

@ -57,6 +57,25 @@
PAPERLESS_OCR_LANGUAGE.value = "nld"; PAPERLESS_OCR_LANGUAGE.value = "nld";
USERMAP_UID.value = "33"; USERMAP_UID.value = "33";
USERMAP_GID.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 = { PAPERLESS_DBPASS.valueFrom.secretKeyRef = {
name = "database"; name = "database";

View file

@ -45,15 +45,15 @@
mountPath = "/config"; mountPath = "/config";
} }
{ {
name = "music"; name = "keepassxc";
mountPath = "/music"; mountPath = "/keepassxc";
} }
]; ];
}; };
volumes = { volumes = {
config.persistentVolumeClaim.claimName = "config"; config.persistentVolumeClaim.claimName = "config";
music.persistentVolumeClaim.claimName = "music"; keepassxc.persistentVolumeClaim.claimName = "keepassxc";
}; };
securityContext = { securityContext = {
@ -74,19 +74,19 @@
targetPort = "web"; targetPort = "web";
}; };
}; };
persistentVolumeClaims.music.spec = {
accessModes = ["ReadWriteMany"];
storageClassName = "";
resources.requests.storage = "1Mi";
volumeName = "music-syncthing";
};
}; };
lab = { lab = {
longhorn.persistentVolumeClaim.config = { longhorn.persistentVolumeClaim = {
volumeName = "syncthing"; config = {
storage = "400Mi"; volumeName = "syncthing";
storage = "400Mi";
};
keepassxc = {
volumeName = "keepassxc";
storage = "100Mi";
};
}; };
tailscaleIngresses.tailscale = { tailscaleIngresses.tailscale = {

View file

@ -42,7 +42,7 @@
pathType = "Prefix"; pathType = "Prefix";
backend.service = { backend.service = {
name = service.name; inherit (service) name;
port.name = service.portName; port.name = service.portName;
}; };
} }

View file

@ -20,7 +20,7 @@
ports = { ports = {
localsecure = { localsecure = {
port = 8444; port = 8444;
expose = true; expose.default = true;
exposedPort = 444; exposedPort = 444;
protocol = "TCP"; protocol = "TCP";

View file

@ -0,0 +1,12 @@
{
dinit.enable = true;
init.services.atuin.shutdownOnExit = true;
services.atuin = {
enable = true;
settings = {
open_registration = false;
};
};
}

View file

@ -1,9 +1,23 @@
{...}: { {
lib,
nglib,
config,
...
}: {
dinit.enable = true; dinit.enable = true;
init.services.bazarr.shutdownOnExit = true;
init.services.bazarr = {
shutdownOnExit = true;
group = lib.mkForce "media";
};
services.bazarr = { services.bazarr = {
enable = true; enable = true;
configDir = "/config"; configDir = "/config";
}; };
users.groups.media = nglib.mkDefaultRec {
gid = config.ids.gids.media;
members = ["bazarr"];
};
} }

View file

@ -5,9 +5,6 @@
nginx, nginx,
blog, blog,
nixpkgs, nixpkgs,
nixpkgs-jellyseerr,
nixpkgs-bazarr,
nixpkgs-radicale,
nixpkgs-master, nixpkgs-master,
... ...
}: }:
@ -23,6 +20,9 @@ flake-utils.lib.eachDefaultSystem (system: let
bazarr = ./bazarr.nix; bazarr = ./bazarr.nix;
prowlarr = ./prowlarr.nix; prowlarr = ./prowlarr.nix;
blog = ./blog.nix; blog = ./blog.nix;
deluge = ./deluge.nix;
mealie = ./mealie.nix;
atuin = ./atuin.nix;
}; };
in { in {
nixngConfigurations = builtins.mapAttrs (name: configFile: nixngConfigurations = builtins.mapAttrs (name: configFile:
@ -37,16 +37,20 @@ in {
}; };
extraModules = [ 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.atuin
{ {
nixpkgs.overlays = [ nixpkgs.overlays = [
(final: _prev: { (_final: _prev: {
# From master branch # From master branch
prowlarr = nixpkgs-master.legacyPackages.${system}.prowlarr; inherit (nixpkgs-master.legacyPackages.${system}) jellyseerr radicale bazarr prowlarr;
# From forks
bazarr = nixpkgs-bazarr.legacyPackages.${system}.bazarr;
jellyseerr = nixpkgs-jellyseerr.legacyPackages.${system}.jellyseerr;
radicale = nixpkgs-radicale.legacyPackages.${system}.radicale;
}) })
]; ];
} }

View file

@ -0,0 +1,30 @@
{
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"];
};
}

View file

@ -1,9 +1,22 @@
{...}: { {
config,
lib,
nglib,
...
}: {
dinit.enable = true; dinit.enable = true;
init.services.jellyseerr.shutdownOnExit = true; init.services.jellyseerr = {
shutdownOnExit = true;
group = lib.mkForce "media";
};
services.jellyseerr = { services.jellyseerr = {
enable = true; enable = true;
configDir = "/app/config"; configDir = "/app/config";
}; };
users.groups.media = nglib.mkDefaultRec {
gid = config.ids.gids.media;
members = ["jellyseerr"];
};
} }

View file

@ -0,0 +1,25 @@
{
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";
};
};
}

View file

@ -1,4 +1,4 @@
{...}: { {
dinit.enable = true; dinit.enable = true;
init.services.ntfy-sh.shutdownOnExit = true; init.services.ntfy-sh.shutdownOnExit = true;

View file

@ -1,9 +1,22 @@
{...}: { {
lib,
nglib,
config,
...
}: {
dinit.enable = true; dinit.enable = true;
init.services.prowlarr.shutdownOnExit = true; init.services.prowlarr = {
shutdownOnExit = true;
group = lib.mkForce "media";
};
services.prowlarr = { services.prowlarr = {
enable = true; enable = true;
dataDir = "/config"; dataDir = "/config";
}; };
users.groups.media = nglib.mkDefaultRec {
gid = config.ids.gids.media;
members = ["prowlarr"];
};
} }

View file

@ -1,9 +1,22 @@
{...}: { {
lib,
nglib,
config,
...
}: {
dinit.enable = true; dinit.enable = true;
init.services.radarr.shutdownOnExit = true; init.services.radarr = {
shutdownOnExit = true;
group = lib.mkForce "media";
};
services.radarr = { services.radarr = {
enable = true; enable = true;
dataDir = "/config"; dataDir = "/config";
}; };
users.groups.media = nglib.mkDefaultRec {
gid = config.ids.gids.media;
members = ["radarr"];
};
} }

View file

@ -1,9 +1,22 @@
{...}: { {
lib,
config,
nglib,
...
}: {
dinit.enable = true; dinit.enable = true;
init.services.sonarr.shutdownOnExit = true; init.services.sonarr = {
shutdownOnExit = true;
group = lib.mkForce "media";
};
services.sonarr = { services.sonarr = {
enable = true; enable = true;
dataDir = "/config"; dataDir = "/config";
}; };
users.groups.media = nglib.mkDefaultRec {
gid = config.ids.gids.media;
members = ["sonarr"];
};
} }

86
nixng-modules/atuin.nix Normal file
View file

@ -0,0 +1,86 @@
{
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;};
};
}

49
nixng-modules/bazarr.nix Normal file
View file

@ -0,0 +1,49 @@
{
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;});
};
}

14
nixng-modules/default.nix Normal file
View file

@ -0,0 +1,14 @@
_: {
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;
};
}

85
nixng-modules/deluge.nix Normal file
View file

@ -0,0 +1,85 @@
{
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";
group = 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;});
};
}

28
nixng-modules/ids.nix Normal file
View file

@ -0,0 +1,28 @@
{
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;
};
};
}

View file

@ -0,0 +1,65 @@
{
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;});
};
}

85
nixng-modules/mealie.nix Normal file
View file

@ -0,0 +1,85 @@
{
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;};
};
}

View file

@ -0,0 +1,48 @@
{
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;});
};
}

47
nixng-modules/radarr.nix Normal file
View file

@ -0,0 +1,47 @@
{
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;});
};
}

View file

@ -0,0 +1,71 @@
{
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;};
});
}

48
nixng-modules/sonarr.nix Normal file
View file

@ -0,0 +1,48 @@
{
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;});
};
}

View file

@ -13,6 +13,11 @@ if [ -z "$username" ] || [ -z "$host" ]
exit 1 exit 1
fi fi
if [ ! -d "$output_path" ]; then
echo "Output directory $output_path does not exist!"
exit 1
fi
# Create a temporary directory # Create a temporary directory
temp=$(mktemp -d) temp=$(mktemp -d)

View file

@ -1,11 +1,16 @@
freshrss: freshrss:
password: ENC[AES256_GCM,data:ECDPrW+VgO8PY9p2fLIreRETNiRL5ZGnu/PMC7aNj8KaWfyNYL+l3w==,iv:srR/r1EtOpC/CKKrCDKcTLVdMFPAYIJIB1CCg8mS0UU=,tag:YN4PqR5uvPkVskpJWD+91g==,type:str] 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: pihole:
password: ENC[AES256_GCM,data:MA60825Tl6aYEFVoPgo8k5Vjb9zmIxtPLJriQV1B3P1bOKu1KK7vxQ==,iv:RGZHox8CbJiEEEjMo2k/tNbtjCPy/QY7vOuMN/YNZcg=,tag:yphrq03IKpXM/tSDBLeSgA==,type:str] password: ENC[AES256_GCM,data:MA60825Tl6aYEFVoPgo8k5Vjb9zmIxtPLJriQV1B3P1bOKu1KK7vxQ==,iv:RGZHox8CbJiEEEjMo2k/tNbtjCPy/QY7vOuMN/YNZcg=,tag:yphrq03IKpXM/tSDBLeSgA==,type:str]
hedgedoc: hedgedoc:
databaseURL: ENC[AES256_GCM,data:6+IV4TaClIGE1XVkUf7JwXzqx3EvWiIKFx9X5x7QKvQKC7bIieD1ADVeAMQmiQfibnH/YV5TgjNY8Ft+3eX881c3yD+2j7mM+O1fX6taK/BCokDnqhIwTN2qxHsu+mrPcM/Pgg5Zqy8HvUgX8jM=,iv:bCwuNk5CVgK2T5IgLebcKwxwloi6FkWMWhnxwJek1GM=,tag:UDQ0KmRDVlDh35Fjm6eaAA==,type:str] 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] 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] 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: nextcloud:
databasePassword: ENC[AES256_GCM,data:jRLgW96FnMEpU0T5z/iQOX/CgjpH2ZykZGd1qGFHK8o=,iv:YrY9IsrlCaiQ8BFYqu+UnOxnvvB/JN4iYfy3vMa3wcw=,tag:41iWc4iVqjdUr02O5CLu7g==,type:str] databasePassword: ENC[AES256_GCM,data:jRLgW96FnMEpU0T5z/iQOX/CgjpH2ZykZGd1qGFHK8o=,iv:YrY9IsrlCaiQ8BFYqu+UnOxnvvB/JN4iYfy3vMa3wcw=,tag:41iWc4iVqjdUr02O5CLu7g==,type:str]
paperless: paperless:
@ -29,6 +34,29 @@ immich:
tailscale: tailscale:
clientID: ENC[AES256_GCM,data:O8tTyy55xP85JkbJNR5daB4=,iv:SMj83Sxh7BvPRG3l5TnnpmclO5N2treUQCCJuMy8cO8=,tag:UUSN3bsZvb09cyYN65RQDg==,type:str] 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] 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: sops:
kms: [] kms: []
gcp_kms: [] gcp_kms: []
@ -53,8 +81,8 @@ sops:
azR0UkJyL0RwUVk4ZzdkSWptcDlWVjAK5FU9B5TBSnV3azO4eCv13T6i3dGGuI68 azR0UkJyL0RwUVk4ZzdkSWptcDlWVjAK5FU9B5TBSnV3azO4eCv13T6i3dGGuI68
UgBrVEb1/Fv+4XTjeSEhpiOaH8sNWYoNa3Aa7uTZYlHDRWga2GC7zw== UgBrVEb1/Fv+4XTjeSEhpiOaH8sNWYoNa3Aa7uTZYlHDRWga2GC7zw==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
lastmodified: "2024-12-01T13:22:41Z" lastmodified: "2025-02-15T15:37:53Z"
mac: ENC[AES256_GCM,data:6UqmxHJC4KWsiQttXFEEG1opPcrGntYj9nlD8m0iBqjc9g/SHxEogpaiYEnriGNXGw0HhRWjrd+JX29Ht4xVeiYqthYX+4rVuIuv+SI7p08hJeIBbIYrfonAJsebbSsynuy9YgyUkNZhoqjZTtuzFU/c4Dh5453RVnuQmu4PZNs=,iv:yA//mqJ0Ft63eRME8A1HBiZ/B0gcVYlS4MaP0LykooU=,tag:0NxU0lVi67N34eDhsT82kQ==,type:str] mac: ENC[AES256_GCM,data:tsoDYbuhxEH3PrxOPgfKczD8Hh1XGJRhGAtm2DWpPP9T99ub/l3KAV2pInvUi5Kn+1QvhJUAwFAP6A/435cqfsHxQI066N7ADUYO4qshcsAYKK7ofBVNnI431D3oD+kBujWKmvSqhlamdP+O7O1ICtbfI5PEM8SN5KWEvEtyp9A=,iv:pDiPy6EWLaZQbNydRFTktRlcf7M9Uf8OS+WPbQkUx9M=,tag:D+tMTFVbWE7TQIw/0MUZjw==,type:str]
pgp: [] pgp: []
unencrypted_suffix: _unencrypted unencrypted_suffix: _unencrypted
version: 3.9.1 version: 3.9.4