From df0e38125e7b7ffaafcde324a53db91672dee849 Mon Sep 17 00:00:00 2001 From: voidarc Date: Sun, 2 Aug 2026 18:41:32 +0100 Subject: [PATCH] from my phone so i cant see the changes lol --- modules/containers/arr/container.nix | 364 ++++++++++++++++++ modules/containers/blog/container.nix | 67 ++++ modules/containers/copyparty/container.nix | 70 ++++ modules/containers/cryptpad/container.nix | 75 ++++ modules/containers/default.nix | 26 +- modules/containers/gitea/container.nix | 108 ++++++ modules/containers/gotify/container.nix | 62 +++ modules/containers/immich/container.nix | 194 ++++++++++ modules/containers/jellyfin/container.nix | 69 ++++ modules/containers/komga/container.nix | 63 +++ modules/containers/mail/container.nix | 88 +++++ modules/containers/n8n/container.nix | 86 +++++ modules/containers/nginx-ha/container.nix | 70 ++++ modules/containers/silverbullet/container.nix | 61 +++ modules/features/certbot/default.nix | 15 + .../{secrets => features/sops}/default.nix | 2 +- modules/hosts/server02/default.nix | 1 + 17 files changed, 1419 insertions(+), 2 deletions(-) create mode 100644 modules/containers/arr/container.nix create mode 100644 modules/containers/blog/container.nix create mode 100644 modules/containers/copyparty/container.nix create mode 100644 modules/containers/cryptpad/container.nix create mode 100644 modules/containers/gitea/container.nix create mode 100644 modules/containers/gotify/container.nix create mode 100644 modules/containers/immich/container.nix create mode 100644 modules/containers/jellyfin/container.nix create mode 100644 modules/containers/komga/container.nix create mode 100644 modules/containers/mail/container.nix create mode 100644 modules/containers/n8n/container.nix create mode 100644 modules/containers/nginx-ha/container.nix create mode 100644 modules/containers/silverbullet/container.nix create mode 100644 modules/features/certbot/default.nix rename modules/{secrets => features/sops}/default.nix (96%) diff --git a/modules/containers/arr/container.nix b/modules/containers/arr/container.nix new file mode 100644 index 0000000..4adfe4f --- /dev/null +++ b/modules/containers/arr/container.nix @@ -0,0 +1,364 @@ +{moduleWithSystem, ...}: { + flake.nixosModules.containerArrStack = moduleWithSystem ({pkgs, ...}: {config, ...}: { + virtualisation.oci-containers.containers."bazarr" = { + image = "lscr.io/linuxserver/bazarr:latest"; + environment = { + "PGID" = "0"; + "PUID" = "0"; + "TZ" = "Etc/UTC"; + }; + volumes = [ + "/containers/arr/bazarr-config:/config:rw" + "/mnt/storage/streaming:/data:rw" + ]; + ports = [ + "8994:6767/tcp" + ]; + log-driver = "journald"; + extraOptions = [ + "--network-alias=bazarr" + "--network=arr-stack_default" + ]; + }; + systemd.services."podman-bazarr" = { + serviceConfig = { + Restart = pkgs.lib.mkOverride 90 "always"; + }; + after = [ + "podman-network-arr-stack_default.service" + ]; + requires = [ + "podman-network-arr-stack_default.service" + ]; + partOf = [ + "podman-compose-arr-stack-root.target" + ]; + wantedBy = [ + "podman-compose-arr-stack-root.target" + ]; + }; + virtualisation.oci-containers.containers."flaresolverr" = { + image = "ghcr.io/flaresolverr/flaresolverr:latest"; + environment = { + "CAPTCHA_SOLVER" = "none"; + "LOG_HTML" = "false"; + "LOG_LEVEL" = "info"; + "TZ" = "Europe/London"; + }; + ports = [ + "8191:8191/tcp" + ]; + log-driver = "journald"; + extraOptions = [ + "--network-alias=flaresolverr" + "--network=arr-stack_default" + ]; + }; + systemd.services."podman-flaresolverr" = { + serviceConfig = { + Restart = pkgs.lib.mkOverride 90 "always"; + }; + after = [ + "podman-network-arr-stack_default.service" + ]; + requires = [ + "podman-network-arr-stack_default.service" + ]; + partOf = [ + "podman-compose-arr-stack-root.target" + ]; + wantedBy = [ + "podman-compose-arr-stack-root.target" + ]; + }; + virtualisation.oci-containers.containers."lidarr" = { + image = "lscr.io/linuxserver/lidarr:nightly"; + environment = { + "PGID" = "0"; + "PUID" = "0"; + "TZ" = "Europe/London"; + }; + volumes = [ + "/containers/arr/lidarr-config:/config:rw" + "/mnt/storage/streaming:/data:rw" + ]; + ports = [ + "8992:8686/tcp" + ]; + log-driver = "journald"; + extraOptions = [ + "--network-alias=lidarr" + "--network=arr-stack_default" + ]; + }; + systemd.services."podman-lidarr" = { + serviceConfig = { + Restart = pkgs.lib.mkOverride 90 "always"; + }; + after = [ + "podman-network-arr-stack_default.service" + ]; + requires = [ + "podman-network-arr-stack_default.service" + ]; + partOf = [ + "podman-compose-arr-stack-root.target" + ]; + wantedBy = [ + "podman-compose-arr-stack-root.target" + ]; + }; + virtualisation.oci-containers.containers."lidatube" = { + image = "thewicklowwolf/lidatube:latest"; + environment = { + "PGID" = "0"; + "PUID" = "0"; + "lidarr_address" = "http://192.168.1.180:8992"; + }; + environmentFiles = [config.sops.secrets.lidarr-api-key.path]; + volumes = [ + "/containers/arr/lidatube-config:/lidatube/config:rw" + "/etc/localtime:/etc/localtime:ro" + "/mnt/storage/streaming/lidarr:/lidatube/downloads:rw" + ]; + ports = [ + "8997:5000/tcp" + ]; + log-driver = "journald"; + extraOptions = [ + "--network-alias=lidatube" + "--network=arr-stack_default" + ]; + }; + systemd.services."podman-lidatube" = { + serviceConfig = { + Restart = pkgs.lib.mkOverride 90 "always"; + }; + after = [ + "podman-network-arr-stack_default.service" + ]; + requires = [ + "podman-network-arr-stack_default.service" + ]; + partOf = [ + "podman-compose-arr-stack-root.target" + ]; + wantedBy = [ + "podman-compose-arr-stack-root.target" + ]; + }; + virtualisation.oci-containers.containers."nginx" = { + image = "lscr.io/linuxserver/nginx:latest"; + environment = { + "NGINX_AUTORELOAD" = "true"; + "PGID" = "1000"; + "PUID" = "1000"; + "TZ" = "Europe/London"; + }; + volumes = [ + "/containers/arr/nginx-config:/config:rw" + ]; + ports = [ + "8989:80/tcp" + ]; + log-driver = "journald"; + extraOptions = [ + "--network-alias=nginx" + "--network=arr-stack_default" + ]; + }; + systemd.services."podman-nginx" = { + serviceConfig = { + Restart = pkgs.lib.mkOverride 90 "always"; + }; + after = [ + "podman-network-arr-stack_default.service" + ]; + requires = [ + "podman-network-arr-stack_default.service" + ]; + partOf = [ + "podman-compose-arr-stack-root.target" + ]; + wantedBy = [ + "podman-compose-arr-stack-root.target" + ]; + }; + virtualisation.oci-containers.containers."prowlarr" = { + image = "lscr.io/linuxserver/prowlarr:latest"; + environment = { + "PGID" = "1000"; + "PUID" = "1000"; + "TZ" = "Etc/UTC"; + }; + volumes = [ + "/containers/arr/prowlarr-config:/config:rw" + ]; + ports = [ + "8996:9696/tcp" + ]; + log-driver = "journald"; + extraOptions = [ + "--network-alias=prowlarr" + "--network=arr-stack_default" + ]; + }; + systemd.services."podman-prowlarr" = { + serviceConfig = { + Restart = pkgs.lib.mkOverride 90 "always"; + }; + after = [ + "podman-network-arr-stack_default.service" + ]; + requires = [ + "podman-network-arr-stack_default.service" + ]; + partOf = [ + "podman-compose-arr-stack-root.target" + ]; + wantedBy = [ + "podman-compose-arr-stack-root.target" + ]; + }; + virtualisation.oci-containers.containers."qbittorrent" = { + image = "lscr.io/linuxserver/qbittorrent:latest"; + environment = { + "PGID" = "0"; + "PUID" = "0"; + "TORRENTING_PORT" = "6881"; + "TZ" = "Etc/UTC"; + "WEBUI_PORT" = "8995"; + }; + volumes = [ + "/containers/arr/qbittorrnt-config:/config:rw" + "/mnt/storage/streaming/downloadclient:/data/downloadclient:rw" + ]; + ports = [ + "8995:8995/tcp" + "6881:6881/tcp" + "6881:6881/udp" + ]; + log-driver = "journald"; + extraOptions = [ + "--network-alias=qbittorrent" + "--network=arr-stack_default" + ]; + }; + systemd.services."podman-qbittorrent" = { + serviceConfig = { + Restart = pkgs.lib.mkOverride 90 "always"; + }; + after = [ + "podman-network-arr-stack_default.service" + ]; + requires = [ + "podman-network-arr-stack_default.service" + ]; + partOf = [ + "podman-compose-arr-stack-root.target" + ]; + wantedBy = [ + "podman-compose-arr-stack-root.target" + ]; + }; + virtualisation.oci-containers.containers."radarr" = { + image = "lscr.io/linuxserver/radarr:latest"; + environment = { + "PGID" = "0"; + "PUID" = "0"; + "TZ" = "Europe/London"; + }; + volumes = [ + "/containers/arr/radarr-config:/config:rw" + "/mnt/storage/streaming:/data:rw" + ]; + ports = [ + "8991:7878/tcp" + ]; + log-driver = "journald"; + extraOptions = [ + "--network-alias=radarr" + "--network=arr-stack_default" + ]; + }; + systemd.services."podman-radarr" = { + serviceConfig = { + Restart = pkgs.lib.mkOverride 90 "always"; + }; + after = [ + "podman-network-arr-stack_default.service" + ]; + requires = [ + "podman-network-arr-stack_default.service" + ]; + partOf = [ + "podman-compose-arr-stack-root.target" + ]; + wantedBy = [ + "podman-compose-arr-stack-root.target" + ]; + }; + virtualisation.oci-containers.containers."sonarr" = { + image = "lscr.io/linuxserver/sonarr:latest"; + environment = { + "PGID" = "0"; + "PUID" = "0"; + "TZ" = "Europe/London"; + }; + volumes = [ + "/containers/arr/sonarr-config:/config:rw" + "/mnt/storage/streaming:/data:rw" + ]; + ports = [ + "8990:8989/tcp" + ]; + log-driver = "journald"; + extraOptions = [ + "--network-alias=sonarr" + "--network=arr-stack_default" + ]; + }; + systemd.services."podman-sonarr" = { + serviceConfig = { + Restart = pkgs.lib.mkOverride 90 "always"; + }; + after = [ + "podman-network-arr-stack_default.service" + ]; + requires = [ + "podman-network-arr-stack_default.service" + ]; + partOf = [ + "podman-compose-arr-stack-root.target" + ]; + wantedBy = [ + "podman-compose-arr-stack-root.target" + ]; + }; + + # Networks + systemd.services."podman-network-arr-stack_default" = { + path = [pkgs.podman]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + ExecStop = "podman network rm -f arr-stack_default"; + }; + script = '' + podman network inspect arr-stack_default || podman network create arr-stack_default + ''; + partOf = ["podman-compose-arr-stack-root.target"]; + wantedBy = ["podman-compose-arr-stack-root.target"]; + }; + + # Root service + # When started, this will automatically create all resources and start + # the containers. When stopped, this will teardown all resources. + systemd.targets."podman-compose-arr-stack-root" = { + unitConfig = { + Description = "Root target generated by compose2nix."; + }; + wantedBy = ["multi-user.target"]; + }; + }); +} diff --git a/modules/containers/blog/container.nix b/modules/containers/blog/container.nix new file mode 100644 index 0000000..d35f7fa --- /dev/null +++ b/modules/containers/blog/container.nix @@ -0,0 +1,67 @@ +{moduleWithSystem, ...}: { + flake.nixosModules.containerBlog = moduleWithSystem ({pkgs, ...}: { + virtualisation.oci-containers.containers."nginx-blog" = { + image = "lscr.io/linuxserver/nginx:latest"; + environment = { + "NGINX_AUTORELOAD" = "true"; + "NGINX_AUTORELOAD_WATCHLIST" = ""; + "PGID" = "1000"; + "PUID" = "1000"; + "TZ" = "Etc/UTC"; + }; + volumes = [ + "/containers/blog/nginx:/config:rw" + ]; + ports = [ + "3030:80/tcp" + ]; + log-driver = "journald"; + extraOptions = [ + "--network-alias=nginx" + "--network=blog_default" + ]; + }; + systemd.services."podman-nginx-blog" = { + serviceConfig = { + Restart = pkgs.lib.mkOverride 90 "always"; + }; + after = [ + "podman-network-blog_default.service" + ]; + requires = [ + "podman-network-blog_default.service" + ]; + partOf = [ + "podman-compose-blog-root.target" + ]; + wantedBy = [ + "podman-compose-blog-root.target" + ]; + }; + + # Networks + systemd.services."podman-network-blog_default" = { + path = [pkgs.podman]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + ExecStop = "podman network rm -f blog_default"; + }; + script = '' + podman network inspect blog_default || podman network create blog_default + ''; + partOf = ["podman-compose-blog-root.target"]; + wantedBy = ["podman-compose-blog-root.target"]; + }; + + # Root service + # When started, this will automatically create all resources and start + # the containers. When stopped, this will teardown all resources. + systemd.targets."podman-compose-blog-root" = { + unitConfig = { + Description = "Root target generated by compose2nix."; + }; + wantedBy = ["multi-user.target"]; + }; + }); +} diff --git a/modules/containers/copyparty/container.nix b/modules/containers/copyparty/container.nix new file mode 100644 index 0000000..42431aa --- /dev/null +++ b/modules/containers/copyparty/container.nix @@ -0,0 +1,70 @@ +{moduleWithSystem, ...}: { + flake.nixosModules.containerCopyparty = moduleWithSystem ({pkgs, ...}: { + virtualisation.oci-containers.containers."copyparty" = { + image = "copyparty/ac:latest"; + environment = { + "LD_PRELOAD" = "/usr/lib/libmimalloc-secure.so.NOPE"; + }; + volumes = [ + "/containers/copyparty:/cfg:rw,z" + "/mnt/storage:/w:rw,z" + ]; + ports = [ + "8001:3923/tcp" + ]; + user = "0:0"; + log-driver = "journald"; + extraOptions = [ + "--health-cmd=wget --spider -q 127.0.0.1:3923/?reset=/._" + "--health-interval=1m0s" + "--health-retries=5" + "--health-start-period=15s" + "--health-timeout=2s" + "--network-alias=copyparty" + "--network=copyparty_default" + ]; + }; + systemd.services."podman-copyparty" = { + serviceConfig = { + Restart = pkgs.lib.mkOverride 90 "always"; + }; + after = [ + "podman-network-copyparty_default.service" + ]; + requires = [ + "podman-network-copyparty_default.service" + ]; + partOf = [ + "podman-compose-copyparty-root.target" + ]; + wantedBy = [ + "podman-compose-copyparty-root.target" + ]; + }; + + # Networks + systemd.services."podman-network-copyparty_default" = { + path = [pkgs.podman]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + ExecStop = "podman network rm -f copyparty_default"; + }; + script = '' + podman network inspect copyparty_default || podman network create copyparty_default + ''; + partOf = ["podman-compose-copyparty-root.target"]; + wantedBy = ["podman-compose-copyparty-root.target"]; + }; + + # Root service + # When started, this will automatically create all resources and start + # the containers. When stopped, this will teardown all resources. + systemd.targets."podman-compose-copyparty-root" = { + unitConfig = { + Description = "Root target generated by compose2nix."; + }; + wantedBy = ["multi-user.target"]; + }; + }); +} diff --git a/modules/containers/cryptpad/container.nix b/modules/containers/cryptpad/container.nix new file mode 100644 index 0000000..6469c2b --- /dev/null +++ b/modules/containers/cryptpad/container.nix @@ -0,0 +1,75 @@ +{moduleWithSystem, ...}: { + flake.nixosModules.containerCryptpad = moduleWithSystem ({pkgs, ...}: { + virtualisation.oci-containers.containers."cryptpad-cryptpad" = { + image = "cryptpad/cryptpad:latest"; + environment = { + "CPAD_CONF" = "/cryptpad/config/config.js"; + "CPAD_INSTALL_ONLYOFFICE" = "yes"; + "CPAD_MAIN_DOMAIN" = "https://office.voidarc.co.uk"; + "CPAD_SANDBOX_DOMAIN" = "https://word.voidarc.co.uk"; + }; + volumes = [ + "/containers/cryptpad/config/config.example.js:/cryptpad/config/config.js:rw" + "/containers/cryptpad/customize:/cryptpad/customize:rw" + "/containers/cryptpad/data/blob:/cryptpad/blob:rw" + "/containers/cryptpad/data/block:/cryptpad/block:rw" + "/containers/cryptpad/data/data:/cryptpad/data:rw" + "/containers/cryptpad/data/files:/cryptpad/datastore:rw" + "/containers/cryptpad/onlyoffice-conf:/cryptpad/onlyoffice-conf:rw" + "/containers/cryptpad/onlyoffice-dist:/cryptpad/www/common/onlyoffice/dist:rw" + ]; + ports = [ + "3000:3000/tcp" + "3003:3003/tcp" + ]; + log-driver = "journald"; + extraOptions = [ + "--hostname=cryptpad" + "--network-alias=cryptpad" + "--network=cryptpad_default" + ]; + }; + systemd.services."podman-cryptpad-cryptpad" = { + serviceConfig = { + Restart = pkgs.lib.mkOverride 90 "always"; + }; + after = [ + "podman-network-cryptpad_default.service" + ]; + requires = [ + "podman-network-cryptpad_default.service" + ]; + partOf = [ + "podman-compose-cryptpad-root.target" + ]; + wantedBy = [ + "podman-compose-cryptpad-root.target" + ]; + }; + + # Networks + systemd.services."podman-network-cryptpad_default" = { + path = [pkgs.podman]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + ExecStop = "podman network rm -f cryptpad_default"; + }; + script = '' + podman network inspect cryptpad_default || podman network create cryptpad_default + ''; + partOf = ["podman-compose-cryptpad-root.target"]; + wantedBy = ["podman-compose-cryptpad-root.target"]; + }; + + # Root service + # When started, this will automatically create all resources and start + # the containers. When stopped, this will teardown all resources. + systemd.targets."podman-compose-cryptpad-root" = { + unitConfig = { + Description = "Root target generated by compose2nix."; + }; + wantedBy = ["multi-user.target"]; + }; + }); +} diff --git a/modules/containers/default.nix b/modules/containers/default.nix index 332e014..3b46d2c 100644 --- a/modules/containers/default.nix +++ b/modules/containers/default.nix @@ -1,6 +1,23 @@ {self, ...}: { - flake.nixosModules.containers = {config, ...}: let + flake.nixosModules.containers = { + pkgs, + config, + ... + }: let modules = with self.nixosModules; [ + containerArrStack + containerBlog + containerCopyparty + containerCryptpad + containerGitea + containerGotify + containerImmich + containerJellyfin + containerMail + containerN8N + containerKomga + containerNginxHomeAssistant + containerSilverbullet ]; in { imports = modules; @@ -11,6 +28,13 @@ dockerCompat = true; }; + environment.systemPackages = with pkgs; [ + podman-compose + ]; + + hardware.nvidia-container-toolkit = { + enable = true; + }; # Enable container name DNS for all Podman networks. networking.firewall.interfaces = let matchAll = diff --git a/modules/containers/gitea/container.nix b/modules/containers/gitea/container.nix new file mode 100644 index 0000000..cbc7582 --- /dev/null +++ b/modules/containers/gitea/container.nix @@ -0,0 +1,108 @@ +{moduleWithSystem, ...}: { + flake.nixosModules.containerGitea = moduleWithSystem ({pkgs, ...}: {config, ...}: { + virtualisation.oci-containers.containers."gitea" = { + image = "docker.gitea.com/gitea:1.25.4"; + environment = { + "GITEA__database__DB_TYPE" = "mysql"; + "GITEA__database__HOST" = "gitea-db:3306"; + "GITEA__database__NAME" = "gitea"; + "GITEA__database__USER" = "gitea"; + }; + environmentFiles = [config.sops.templates."gitea.env".path]; + volumes = [ + "/containers/gitea/gitea:/data:rw" + "/etc/localtime:/etc/localtime:ro" + "/etc/timezone:/etc/timezone:ro" + ]; + ports = [ + "3009:3000/tcp" + "222:22/tcp" + ]; + dependsOn = [ + "gitea-db" + ]; + log-driver = "journald"; + extraOptions = [ + "--network-alias=server" + "--network=gitea_gitea" + ]; + }; + systemd.services."podman-gitea" = { + serviceConfig = { + Restart = pkgs.lib.mkOverride 90 "always"; + }; + after = [ + "podman-network-gitea_gitea.service" + ]; + requires = [ + "podman-network-gitea_gitea.service" + ]; + partOf = [ + "podman-compose-gitea-root.target" + ]; + wantedBy = [ + "podman-compose-gitea-root.target" + ]; + }; + + virtualisation.oci-containers.containers."gitea-db" = { + image = "docker.io/library/mysql:8"; + environment = { + "MYSQL_DATABASE" = "gitea"; + # "MYSQL_PASSWORD" = config.sops.secrets."gitea-db-passwd"; + # "MYSQL_ROOT_PASSWORD" = config.sops.secrets."gitea-db-passwd"; + "MYSQL_USER" = "gitea"; + }; + environmentFiles = [config.sops.templates."gitea.env".path]; + extraOptions = [ + "--network-alias=db" + "--network=gitea_gitea" + ]; + volumes = [ + "/containers/gitea/mysql:/var/lib/mysql:rw" + ]; + }; + systemd.services."podman-gitea-db" = { + serviceConfig = { + Restart = pkgs.lib.mkOverride 90 "always"; + }; + after = [ + "podman-network-gitea_gitea.service" + ]; + requires = [ + "podman-network-gitea_gitea.service" + ]; + partOf = [ + "podman-compose-gitea-root.target" + ]; + wantedBy = [ + "podman-compose-gitea-root.target" + ]; + }; + + # Networks + systemd.services."podman-network-gitea_gitea" = { + path = [pkgs.podman]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + ExecStop = "podman network rm -f gitea_gitea"; + }; + script = '' + podman network inspect gitea_gitea || podman network create gitea_gitea + ''; + partOf = ["podman-compose-gitea-root.target"]; + wantedBy = ["podman-compose-gitea-root.target"]; + }; + + # Root service + # When started, this will automatically create all resources and start + # the containers. When stopped, this will teardown all resources. + systemd.targets."podman-compose-gitea-root" = { + unitConfig = { + Description = "Root target generated by compose2nix."; + }; + wantedBy = ["multi-user.target"]; + }; + }); +} diff --git a/modules/containers/gotify/container.nix b/modules/containers/gotify/container.nix new file mode 100644 index 0000000..560b383 --- /dev/null +++ b/modules/containers/gotify/container.nix @@ -0,0 +1,62 @@ +{moduleWithSystem, ...}: { + flake.nixosModules.containerGotify = moduleWithSystem ({pkgs, ...}: { + virtualisation.oci-containers.containers."gotify-gotify" = { + image = "gotify/server"; + environment = { + }; + volumes = [ + "/containers/gotify/gotify_data:/app/data:rw" + ]; + ports = [ + "8088:80/tcp" + ]; + log-driver = "journald"; + extraOptions = [ + "--network-alias=gotify" + "--network=gotify_default" + ]; + }; + systemd.services."podman-gotify-gotify" = { + serviceConfig = { + Restart = pkgs.lib.mkOverride 90 "always"; + }; + after = [ + "podman-network-gotify_default.service" + ]; + requires = [ + "podman-network-gotify_default.service" + ]; + partOf = [ + "podman-compose-gotify-root.target" + ]; + wantedBy = [ + "podman-compose-gotify-root.target" + ]; + }; + + # Networks + systemd.services."podman-network-gotify_default" = { + path = [pkgs.podman]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + ExecStop = "podman network rm -f gotify_default"; + }; + script = '' + podman network inspect gotify_default || podman network create gotify_default + ''; + partOf = ["podman-compose-gotify-root.target"]; + wantedBy = ["podman-compose-gotify-root.target"]; + }; + + # Root service + # When started, this will automatically create all resources and start + # the containers. When stopped, this will teardown all resources. + systemd.targets."podman-compose-gotify-root" = { + unitConfig = { + Description = "Root target generated by compose2nix."; + }; + wantedBy = ["multi-user.target"]; + }; + }); +} diff --git a/modules/containers/immich/container.nix b/modules/containers/immich/container.nix new file mode 100644 index 0000000..83a9231 --- /dev/null +++ b/modules/containers/immich/container.nix @@ -0,0 +1,194 @@ +{moduleWithSystem, ...}: { + flake.nixosModules.containerImmich = moduleWithSystem ({pkgs, ...}: {config, ...}: { + virtualisation.oci-containers.containers."immich_machine_learning" = { + image = "ghcr.io/immich-app/immich-machine-learning:release"; + environment = { + "DB_DATABASE_NAME" = "immich"; + "DB_DATA_LOCATION" = "./postgres"; + "DB_HOST" = "immich_postgres"; + "DB_PORT" = "5432"; + "DB_USERNAME" = "postgres"; + "EXTERNAL_IMMICH_URL" = "https://immich.voidarc.co.uk"; + "IMMICH_URL" = "http://192.168.1.180:2283"; + "IMMICH_VERSION" = "release"; + "UPLOAD_LOCATION" = "/mnt/storage/streaming/images"; + }; + environmentFiles = [config.sops.templates."immich.env".path]; + volumes = [ + "immich_model-cache:/cache:rw" + ]; + log-driver = "journald"; + extraOptions = [ + "--network-alias=immich-machine-learning" + "--network=immich_default" + ]; + }; + systemd.services."podman-immich_machine_learning" = { + serviceConfig = { + Restart = pkgs.lib.mkOverride 90 "always"; + }; + after = [ + "podman-network-immich_default.service" + "podman-volume-immich_model-cache.service" + ]; + requires = [ + "podman-network-immich_default.service" + "podman-volume-immich_model-cache.service" + ]; + partOf = [ + "podman-compose-immich-root.target" + ]; + wantedBy = [ + "podman-compose-immich-root.target" + ]; + }; + virtualisation.oci-containers.containers."immich_postgres" = { + image = "ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:32324a2f41df5de9efe1af166b7008c3f55646f8d0e00d9550c16c9822366b4a"; + environment = { + "POSTGRES_DB" = "immich"; + "POSTGRES_INITDB_ARGS" = "--data-checksums"; + "POSTGRES_USER" = "postgres"; + }; + environmentFiles = [config.sops.templates."immich.env".path]; + volumes = [ + "/containers/immich/postgres:/var/lib/postgresql/data:rw" + ]; + log-driver = "journald"; + extraOptions = [ + "--network-alias=database" + "--network=immich_default" + "--shm-size=134217728" + ]; + }; + systemd.services."podman-immich_postgres" = { + serviceConfig = { + Restart = pkgs.lib.mkOverride 90 "always"; + }; + after = [ + "podman-network-immich_default.service" + ]; + requires = [ + "podman-network-immich_default.service" + ]; + partOf = [ + "podman-compose-immich-root.target" + ]; + wantedBy = [ + "podman-compose-immich-root.target" + ]; + }; + virtualisation.oci-containers.containers."immich_redis" = { + image = "docker.io/valkey/valkey:8-bookworm@sha256:a137a2b60aca1a75130022d6bb96af423fefae4eb55faf395732db3544803280"; + log-driver = "journald"; + extraOptions = [ + "--health-cmd=redis-cli ping || exit 1" + "--network-alias=redis" + "--network=immich_default" + ]; + }; + systemd.services."podman-immich_redis" = { + serviceConfig = { + Restart = pkgs.lib.mkOverride 90 "always"; + }; + after = [ + "podman-network-immich_default.service" + ]; + requires = [ + "podman-network-immich_default.service" + ]; + partOf = [ + "podman-compose-immich-root.target" + ]; + wantedBy = [ + "podman-compose-immich-root.target" + ]; + }; + virtualisation.oci-containers.containers."immich_server" = { + image = "ghcr.io/immich-app/immich-server:release"; + environment = { + "DB_DATABASE_NAME" = "immich"; + "DB_DATA_LOCATION" = "./postgres"; + "DB_HOST" = "immich_postgres"; + "DB_PORT" = "5432"; + "DB_USERNAME" = "postgres"; + "EXTERNAL_IMMICH_URL" = "https://immich.voidarc.co.uk"; + "IMMICH_URL" = "http://192.168.1.180:2283"; + "IMMICH_VERSION" = "release"; + "UPLOAD_LOCATION" = "/mnt/storage/streaming/images"; + }; + environmentFiles = [config.sops.templates."immich.env".path]; + volumes = [ + "/etc/localtime:/etc/localtime:ro" + "/mnt/storage/streaming/images/:/data:rw" + ]; + ports = [ + "2283:2283/tcp" + ]; + dependsOn = [ + "immich_postgres" + "immich_redis" + ]; + log-driver = "journald"; + extraOptions = [ + "--network-alias=immich-server" + "--network=immich_default" + ]; + }; + systemd.services."podman-immich_server" = { + serviceConfig = { + Restart = pkgs.lib.mkOverride 90 "always"; + }; + after = [ + "podman-network-immich_default.service" + ]; + requires = [ + "podman-network-immich_default.service" + ]; + partOf = [ + "podman-compose-immich-root.target" + ]; + wantedBy = [ + "podman-compose-immich-root.target" + ]; + }; + + # Networks + systemd.services."podman-network-immich_default" = { + path = [pkgs.podman]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + ExecStop = "podman network rm -f immich_default"; + }; + script = '' + podman network inspect immich_default || podman network create immich_default + ''; + partOf = ["podman-compose-immich-root.target"]; + wantedBy = ["podman-compose-immich-root.target"]; + }; + + # Volumes + systemd.services."podman-volume-immich_model-cache" = { + path = [pkgs.podman]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + }; + script = '' + podman volume inspect immich_model-cache || podman volume create immich_model-cache + ''; + partOf = ["podman-compose-immich-root.target"]; + wantedBy = ["podman-compose-immich-root.target"]; + }; + + # Root service + # When started, this will automatically create all resources and start + # the containers. When stopped, this will teardown all resources. + systemd.targets."podman-compose-immich-root" = { + unitConfig = { + Description = "Root target generated by compose2nix."; + }; + wantedBy = ["multi-user.target"]; + }; + }); +} diff --git a/modules/containers/jellyfin/container.nix b/modules/containers/jellyfin/container.nix new file mode 100644 index 0000000..23065c2 --- /dev/null +++ b/modules/containers/jellyfin/container.nix @@ -0,0 +1,69 @@ +{moduleWithSystem, ...}: { + flake.nixosModules.containerJellyfin = moduleWithSystem ({pkgs, ...}: { + virtualisation.oci-containers.containers."jellyfin" = { + image = "jellyfin/jellyfin"; + environment = { + "JELLYFIN_PublishedServerUrl" = "https://watch.voidarc.co.uk"; + }; + volumes = [ + "/containers/jellyfin/cache:/cache:rw" + "/containers/jellyfin/config:/config:rw" + "/mnt/storage/streaming:/media:rw" + ]; + ports = [ + "8096:8096/tcp" + "7359:7359/udp" + ]; + user = "0:0"; + log-driver = "journald"; + extraOptions = [ + "--add-host=host.docker.internal:host-gateway" + "--device=nvidia.com/gpu=all" + "--network-alias=jellyfin" + "--network=jellyfin_default" + ]; + }; + systemd.services."podman-jellyfin" = { + serviceConfig = { + Restart = pkgs.lib.mkOverride 90 "always"; + }; + after = [ + "podman-network-jellyfin_default.service" + ]; + requires = [ + "podman-network-jellyfin_default.service" + ]; + partOf = [ + "podman-compose-jellyfin-root.target" + ]; + wantedBy = [ + "podman-compose-jellyfin-root.target" + ]; + }; + + # Networks + systemd.services."podman-network-jellyfin_default" = { + path = [pkgs.podman]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + ExecStop = "podman network rm -f jellyfin_default"; + }; + script = '' + podman network inspect jellyfin_default || podman network create jellyfin_default + ''; + partOf = ["podman-compose-jellyfin-root.target"]; + wantedBy = ["podman-compose-jellyfin-root.target"]; + }; + + # Root service + # When started, this will automatically create all resources and start + # the containers. When stopped, this will teardown all resources. + systemd.targets."podman-compose-jellyfin-root" = { + unitConfig = { + Description = "Root target generated by compose2nix."; + }; + wantedBy = ["multi-user.target"]; + }; + }); +} diff --git a/modules/containers/komga/container.nix b/modules/containers/komga/container.nix new file mode 100644 index 0000000..a56a84d --- /dev/null +++ b/modules/containers/komga/container.nix @@ -0,0 +1,63 @@ +{moduleWithSystem, ...}: { + flake.nixosModules.containerKomga = moduleWithSystem ({pkgs, ...}: { + + virtualisation.oci-containers.containers."komga" = { + image = "gotson/komga"; + volumes = [ + "/containers/komga/config:/config:rw" + "/etc/timezone:/etc/timezone:ro" + "/mnt/storage/streaming/manga:/data:rw" + ]; + ports = [ + "25600:25600/tcp" + ]; + log-driver = "journald"; + extraOptions = [ + "--network-alias=komga" + "--network=komga_default" + ]; + }; + systemd.services."podman-komga" = { + serviceConfig = { + Restart = pkgs.lib.mkOverride 90 "always"; + }; + after = [ + "podman-network-komga_default.service" + ]; + requires = [ + "podman-network-komga_default.service" + ]; + partOf = [ + "podman-compose-komga-root.target" + ]; + wantedBy = [ + "podman-compose-komga-root.target" + ]; + }; + + # Networks + systemd.services."podman-network-komga_default" = { + path = [ pkgs.podman ]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + ExecStop = "podman network rm -f komga_default"; + }; + script = '' + podman network inspect komga_default || podman network create komga_default + ''; + partOf = [ "podman-compose-komga-root.target" ]; + wantedBy = [ "podman-compose-komga-root.target" ]; + }; + + # Root service + # When started, this will automatically create all resources and start + # the containers. When stopped, this will teardown all resources. + systemd.targets."podman-compose-komga-root" = { + unitConfig = { + Description = "Root target generated by compose2nix."; + }; + wantedBy = [ "multi-user.target" ]; + }; + }); +} diff --git a/modules/containers/mail/container.nix b/modules/containers/mail/container.nix new file mode 100644 index 0000000..527f0fd --- /dev/null +++ b/modules/containers/mail/container.nix @@ -0,0 +1,88 @@ +{moduleWithSystem, ...}: { + flake.nixosModules.containerMail = moduleWithSystem ({pkgs, ...}: {config, ...}: { + # Containers + virtualisation.oci-containers.containers."mailserver" = { + image = "ghcr.io/docker-mailserver/docker-mailserver:latest"; + environment = { + "DEFAULT_RELAY_HOST" = "[smtp-relay.brevo.com]:587"; + "DOMAINNAME" = "voidarc.co.uk"; + "ENABLE_CLAMAV" = "1"; + "ENABLE_FAIL2BAN" = "1"; + "ENABLE_POSTFIX_VIRTUAL_TRANSPORT" = "1"; + "ENABLE_POSTGREY" = "1"; + "ENABLE_SPAMASSASSIN" = "1"; + "HOSTNAME" = "mail.voidarc.co.uk"; + "ONE_DIR" = "1"; + "PERMIT_DOCKER" = "network"; + "POSTMASTER_ADDRESS" = "postmaster@voidarc.co.uk"; + "SPOOF_PROTECTION" = "1"; + "SSL_DOMAIN" = "mail.voidarc.co.uk"; + "SSL_TYPE" = "letsencrypt"; + }; + environmentFiles = [config.sops.templates."mail.env".path]; + volumes = [ + "/containers/mail/config:/tmp/docker-mailserver:rw" + "/containers/mail/maildata:/var/mail:rw" + "/containers/mail/mailstate:/var/mail-state:rw" + "/etc/letsencrypt:/etc/letsencrypt:rw" + "/etc/localtime:/etc/localtime:ro" + ]; + ports = [ + "25:25/tcp" + "143:143/tcp" + "465:465/tcp" + "587:587/tcp" + "993:993/tcp" + ]; + log-driver = "journald"; + extraOptions = [ + "--cap-add=NET_ADMIN" + "--hostname=mail.voidarc.co.uk" + "--network-alias=mail" + "--network=mail_default" + ]; + }; + systemd.services."podman-mailserver" = { + serviceConfig = { + Restart = pkgs.lib.mkOverride 90 "always"; + }; + after = [ + "podman-network-mail_default.service" + ]; + requires = [ + "podman-network-mail_default.service" + ]; + partOf = [ + "podman-compose-mail-root.target" + ]; + wantedBy = [ + "podman-compose-mail-root.target" + ]; + }; + + # Networks + systemd.services."podman-network-mail_default" = { + path = [pkgs.podman]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + ExecStop = "podman network rm -f mail_default"; + }; + script = '' + podman network inspect mail_default || podman network create mail_default + ''; + partOf = ["podman-compose-mail-root.target"]; + wantedBy = ["podman-compose-mail-root.target"]; + }; + + # Root service + # When started, this will automatically create all resources and start + # the containers. When stopped, this will teardown all resources. + systemd.targets."podman-compose-mail-root" = { + unitConfig = { + Description = "Root target generated by compose2nix."; + }; + wantedBy = ["multi-user.target"]; + }; + }); +} diff --git a/modules/containers/n8n/container.nix b/modules/containers/n8n/container.nix new file mode 100644 index 0000000..0eaa5e3 --- /dev/null +++ b/modules/containers/n8n/container.nix @@ -0,0 +1,86 @@ +{moduleWithSystem, ...}: { + flake.nixosModules.containerN8N = moduleWithSystem ({pkgs, ...}: { + virtualisation.oci-containers.containers."n8n-n8n" = { + image = "docker.n8n.io/n8nio/n8n"; + environment = { + "GENERIC_TIMEZONE" = ""; + "N8N_HOST" = "."; + "N8N_PORT" = "5678"; + "N8N_PROTOCOL" = "https"; + "N8N_SECURE_COOKIE" = "false"; + "NODE_ENV" = "production"; + "WEBHOOK_URL" = "https://./"; + }; + volumes = [ + "/containers/n8n/local-files:/files:rw" + "/containers/n8n/n8n-data:/home/node/.n8n:rw" + ]; + ports = [ + "0.0.0.0:5678:5678/tcp" + ]; + labels = { + "traefik.enable" = "false"; + "traefik.http.middlewares.n8n.headers.SSLHost" = ""; + "traefik.http.middlewares.n8n.headers.SSLRedirect" = "true"; + "traefik.http.middlewares.n8n.headers.STSIncludeSubdomains" = "true"; + "traefik.http.middlewares.n8n.headers.STSPreload" = "true"; + "traefik.http.middlewares.n8n.headers.STSSeconds" = "315360000"; + "traefik.http.middlewares.n8n.headers.browserXSSFilter" = "true"; + "traefik.http.middlewares.n8n.headers.contentTypeNosniff" = "true"; + "traefik.http.middlewares.n8n.headers.forceSTSHeader" = "true"; + "traefik.http.routers.n8n.entrypoints" = "web,websecure"; + "traefik.http.routers.n8n.middlewares" = "n8n@docker"; + "traefik.http.routers.n8n.rule" = "Host(`.`)"; + "traefik.http.routers.n8n.tls" = "true"; + "traefik.http.routers.n8n.tls.certresolver" = "mytlschallenge"; + }; + log-driver = "journald"; + extraOptions = [ + "--network-alias=n8n" + "--network=n8n_default" + ]; + }; + systemd.services."podman-n8n-n8n" = { + serviceConfig = { + Restart = pkgs.lib.mkOverride 90 "always"; + }; + after = [ + "podman-network-n8n_default.service" + ]; + requires = [ + "podman-network-n8n_default.service" + ]; + partOf = [ + "podman-compose-n8n-root.target" + ]; + wantedBy = [ + "podman-compose-n8n-root.target" + ]; + }; + + # Networks + systemd.services."podman-network-n8n_default" = { + path = [pkgs.podman]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + ExecStop = "podman network rm -f n8n_default"; + }; + script = '' + podman network inspect n8n_default || podman network create n8n_default + ''; + partOf = ["podman-compose-n8n-root.target"]; + wantedBy = ["podman-compose-n8n-root.target"]; + }; + + # Root service + # When started, this will automatically create all resources and start + # the containers. When stopped, this will teardown all resources. + systemd.targets."podman-compose-n8n-root" = { + unitConfig = { + Description = "Root target generated by compose2nix."; + }; + wantedBy = ["multi-user.target"]; + }; + }); +} diff --git a/modules/containers/nginx-ha/container.nix b/modules/containers/nginx-ha/container.nix new file mode 100644 index 0000000..5d89824 --- /dev/null +++ b/modules/containers/nginx-ha/container.nix @@ -0,0 +1,70 @@ +{moduleWithSystem, ...}: { + flake.nixosModules.containerNginxHomeAssistant = moduleWithSystem ({ + pkgs, + ... + }: { + virtualisation.oci-containers.containers."nginx-home-assistant" = { + image = "lscr.io/linuxserver/nginx:latest"; + environment = { + "NGINX_AUTORELOAD" = "true"; + "PGID" = "0"; + "PUID" = "0"; + "TZ" = "Etc/UTC"; + }; + volumes = [ + "/containers/nginx-ha/config:/config:rw" + ]; + ports = [ + "8123:80/tcp" + ]; + log-driver = "journald"; + extraOptions = [ + "--network-alias=nginx" + "--network=nginx-ha_default" + ]; + }; + systemd.services."podman-nginx-home-assistant" = { + serviceConfig = { + Restart = pkgs.lib.mkOverride 90 "always"; + }; + after = [ + "podman-network-nginx-ha_default.service" + ]; + requires = [ + "podman-network-nginx-ha_default.service" + ]; + partOf = [ + "podman-compose-nginx-ha-root.target" + ]; + wantedBy = [ + "podman-compose-nginx-ha-root.target" + ]; + }; + + # Networks + systemd.services."podman-network-nginx-ha_default" = { + path = [ pkgs.podman ]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + ExecStop = "podman network rm -f nginx-ha_default"; + }; + script = '' + podman network inspect nginx-ha_default || podman network create nginx-ha_default + ''; + partOf = [ "podman-compose-nginx-ha-root.target" ]; + wantedBy = [ "podman-compose-nginx-ha-root.target" ]; + }; + + # Root service + # When started, this will automatically create all resources and start + # the containers. When stopped, this will teardown all resources. + systemd.targets."podman-compose-nginx-ha-root" = { + unitConfig = { + Description = "Root target generated by compose2nix."; + }; + wantedBy = [ "multi-user.target" ]; + }; + + }); +} diff --git a/modules/containers/silverbullet/container.nix b/modules/containers/silverbullet/container.nix new file mode 100644 index 0000000..cdab335 --- /dev/null +++ b/modules/containers/silverbullet/container.nix @@ -0,0 +1,61 @@ +{moduleWithSystem, ...}: { + flake.nixosModules.containerSilverbullet = moduleWithSystem ({pkgs, ...}: {config, ...}: { + virtualisation.oci-containers.containers."silverbullet-silverbullet" = { + image = "ghcr.io/silverbulletmd/silverbullet:v2"; + environmentFiles = [config.sops.templates."silverbullet.env".path]; + volumes = [ + "/mnt/storage/Notes:/space:rw" + ]; + ports = [ + "9003:3000/tcp" + ]; + log-driver = "journald"; + extraOptions = [ + "--network-alias=silverbullet" + "--network=silverbullet_default" + ]; + }; + systemd.services."podman-silverbullet-silverbullet" = { + serviceConfig = { + Restart = pkgs.lib.mkOverride 90 "always"; + }; + after = [ + "podman-network-silverbullet_default.service" + ]; + requires = [ + "podman-network-silverbullet_default.service" + ]; + partOf = [ + "podman-compose-silverbullet-root.target" + ]; + wantedBy = [ + "podman-compose-silverbullet-root.target" + ]; + }; + + # Networks + systemd.services."podman-network-silverbullet_default" = { + path = [pkgs.podman]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + ExecStop = "podman network rm -f silverbullet_default"; + }; + script = '' + podman network inspect silverbullet_default || podman network create silverbullet_default + ''; + partOf = ["podman-compose-silverbullet-root.target"]; + wantedBy = ["podman-compose-silverbullet-root.target"]; + }; + + # Root service + # When started, this will automatically create all resources and start + # the containers. When stopped, this will teardown all resources. + systemd.targets."podman-compose-silverbullet-root" = { + unitConfig = { + Description = "Root target generated by compose2nix."; + }; + wantedBy = ["multi-user.target"]; + }; + }); +} diff --git a/modules/features/certbot/default.nix b/modules/features/certbot/default.nix new file mode 100644 index 0000000..3efff0f --- /dev/null +++ b/modules/features/certbot/default.nix @@ -0,0 +1,15 @@ +{moduleWithSystem, ...}: { + flake.nixosModules.certbot = moduleWithSystem ({pkgs, ...}: { + security.acme.acceptTerms = true; + security.acme.defaults.email = "admin@voidarc.co.uk"; + + security.acme.certs."mail.voidarc.co.uk" = { + domain = "mail.voidarc.co.uk"; + dnsProvider = "cloudflare"; + credentialFiles = { + "RFC2136_TSIG_SECRET_FILE" = "/run/secrets/tsig-secret-example.org"; + } + ; + }; + }); +} diff --git a/modules/secrets/default.nix b/modules/features/sops/default.nix similarity index 96% rename from modules/secrets/default.nix rename to modules/features/sops/default.nix index fe38f1e..0ca7e1d 100644 --- a/modules/secrets/default.nix +++ b/modules/features/sops/default.nix @@ -13,7 +13,7 @@ sops = { age.keyFile = "/etc/sops/age/keys.txt"; - defaultSopsFile = ../../secrets/secrets.yaml; + defaultSopsFile = ../../../secrets/secrets.yaml; secrets = { lidarr-api-key = {}; diff --git a/modules/hosts/server02/default.nix b/modules/hosts/server02/default.nix index 53e9c0d..dffd467 100644 --- a/modules/hosts/server02/default.nix +++ b/modules/hosts/server02/default.nix @@ -13,6 +13,7 @@ nvidiaDrivers containers secrets + certbot ] ++ (with inputs.nix-config.nixosModules; [ nvim