diff --git a/hosts/orion/default.nix b/hosts/orion/default.nix index 26b8274..a471638 100644 --- a/hosts/orion/default.nix +++ b/hosts/orion/default.nix @@ -103,6 +103,7 @@ ]; ataraxia.containers.filestash.enable = true; + ataraxia.containers.media-stack.enable = true; ataraxia.containers.tinyproxy.enable = true; ataraxia.security.acme.enable = true; ataraxia.services.authentik.enable = true; diff --git a/modules/nixos/containers/media-stack/caddy.nix b/modules/nixos/containers/media-stack/caddy.nix new file mode 100644 index 0000000..42145cf --- /dev/null +++ b/modules/nixos/containers/media-stack/caddy.nix @@ -0,0 +1,67 @@ +{ + config, + lib, + pkgs, + ... +}: +let + inherit (lib) mkEnableOption mkIf; + + cfg = config.ataraxia.containers.media-stack; + nas-path = "/media/nas/media-stack"; + + caddyconf = pkgs.writeText "Caddyfile" '' + { + auto_https off + http_port 8180 + } + jellyfin.ataraxiadev.com:8180 { + reverse_proxy jellyfin:8096 + } + qbit.ataraxiadev.com:8180 { + reverse_proxy qbittorrent:8080 + } + medusa.ataraxiadev.com:8180 { + reverse_proxy medusa:8081 + } + jackett.ataraxiadev.com:8180 { + reverse_proxy jackett:9117 + } + sonarr.ataraxiadev.com:8180 { + reverse_proxy sonarr:8989 + } + radarr.ataraxiadev.com:8180 { + reverse_proxy radarr:7878 + } + lidarr.ataraxiadev.com:8180 { + reverse_proxy lidarr:8686 + } + kavita.ataraxiadev.com:8180 { + reverse_proxy kavita:5000 + } + ''; +in +{ + options.ataraxia.containers.media-stack = { + caddy = mkEnableOption "Enable media-caddy container"; + }; + + config = mkIf cfg.caddy { + virtualisation.oci-containers.containers.media-caddy = { + autoStart = true; + # Tags: release-20b7f25, release-2.10.0, release + image = "ghcr.io/hotio/caddy@sha256:937fe02672e7ce7f189e28d45c4ccfe86b2a7d5791b4e04badb55e143e32d5b7"; + environment = { + PUID = "1000"; + PGID = "100"; + UMASK = "002"; + TZ = "Europe/Moscow"; + }; + extraOptions = [ "--pod=media-stack" ]; + volumes = [ + "${nas-path}/configs/caddy:/config" + "${caddyconf}:/config/Caddyfile" + ]; + }; + }; +} diff --git a/modules/nixos/containers/media-stack/default.nix b/modules/nixos/containers/media-stack/default.nix new file mode 100644 index 0000000..d6afec0 --- /dev/null +++ b/modules/nixos/containers/media-stack/default.nix @@ -0,0 +1,94 @@ +{ + config, + lib, + pkgs, + ... +}: +let + inherit (lib) + mkDefault + mkEnableOption + mkIf + optionals + ; + + cfg = config.ataraxia.containers.media-stack; + + backend = config.virtualisation.oci-containers.backend; + pod-name = "media-stack"; + open-ports = [ + # caddy + "127.0.0.1:8180:8180" + # qbittorrent + "0.0.0.0:7000:7000" + "0.0.0.0:7000:7000/udp" + ]; + pod-dns = "10.10.10.1"; +in +{ + imports = [ + ./caddy.nix + ./jackett.nix + ./jellyfin.nix + ./kavita.nix + ./lidarr.nix + ./medusa.nix + ./qbittorrent.nix + ./radarr.nix + ./recyclarr.nix + ./sonarr.nix + ]; + + options.ataraxia.containers.media-stack = { + enable = mkEnableOption "Enable media-stack containers"; + }; + + config = mkIf cfg.enable { + ataraxia.containers.media-stack.caddy = mkDefault true; + ataraxia.containers.media-stack.jackett = mkDefault true; + ataraxia.containers.media-stack.jellyfin = mkDefault true; + ataraxia.containers.media-stack.kavita = mkDefault true; + ataraxia.containers.media-stack.lidarr = mkDefault true; + ataraxia.containers.media-stack.medusa = mkDefault true; + ataraxia.containers.media-stack.qbittorrent = mkDefault true; + ataraxia.containers.media-stack.radarr = mkDefault true; + ataraxia.containers.media-stack.recyclarr = mkDefault true; + ataraxia.containers.media-stack.sonarr = mkDefault true; + + systemd.services."podman-create-${pod-name}" = + let + portsMapping = lib.concatMapStrings (port: " -p " + port) open-ports; + start = pkgs.writeShellScript "create-pod-${pod-name}" '' + podman pod exists ${pod-name} || podman pod create -n ${pod-name} ${portsMapping} --dns ${pod-dns} + ''; + stop = pkgs.writeShellScript "remove-pod-${pod-name}" '' + podman pod rm -i -f ${pod-name} + ''; + in + rec { + path = [ + pkgs.coreutils + config.virtualisation.podman.package + ]; + before = + [ ] + ++ optionals cfg.caddy [ "${backend}-media-caddy.service" ] + ++ optionals cfg.jackett [ "${backend}-jackett.service" ] + ++ optionals cfg.jellyfin [ "${backend}-jellyfin.service" ] + ++ optionals cfg.kavita [ "${backend}-kavita.service" ] + ++ optionals cfg.lidarr [ "${backend}-lidarr.service" ] + ++ optionals cfg.medusa [ "${backend}-medusa.service" ] + ++ optionals cfg.qbittorrent [ "${backend}-qbittorrent.service" ] + ++ optionals cfg.radarr [ "${backend}-radarr.service" ] + ++ optionals cfg.recyclarr [ "${backend}-recyclarr.service" ] + ++ optionals cfg.sonarr [ "${backend}-sonarr.service" ]; + requiredBy = before; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = "yes"; + ExecStart = start; + ExecStop = stop; + }; + }; + }; +} diff --git a/modules/nixos/containers/media-stack/jackett.nix b/modules/nixos/containers/media-stack/jackett.nix new file mode 100644 index 0000000..3850469 --- /dev/null +++ b/modules/nixos/containers/media-stack/jackett.nix @@ -0,0 +1,30 @@ +{ config, lib, ... }: +let + inherit (lib) mkEnableOption mkIf; + + cfg = config.ataraxia.containers.media-stack; + nas-path = "/media/nas/media-stack"; +in +{ + options.ataraxia.containers.media-stack = { + jackett = mkEnableOption "Enable jackett container"; + }; + + config = mkIf cfg.jackett { + virtualisation.oci-containers.containers.jackett = { + autoStart = true; + environment = { + PUID = "1000"; + PGID = "100"; + UMASK = "002"; + TZ = "Europe/Moscow"; + }; + extraOptions = [ "--pod=media-stack" ]; + # Tags: 0.22.2117, version-v0.22.2117, v0.22.2117-ls80 + image = "docker.io/linuxserver/jackett@sha256:221606b0ed7df0d66e601d0ba83f5f9cc9b9c761bafad3507d6854406b3a447b"; + volumes = [ + "${nas-path}/configs/jackett:/config" + ]; + }; + }; +} diff --git a/modules/nixos/containers/media-stack/jellyfin.nix b/modules/nixos/containers/media-stack/jellyfin.nix new file mode 100644 index 0000000..9871a42 --- /dev/null +++ b/modules/nixos/containers/media-stack/jellyfin.nix @@ -0,0 +1,51 @@ +{ + config, + lib, + pkgs, + ... +}: +let + inherit (lib) mkEnableOption mkIf; + + cfg = config.ataraxia.containers.media-stack; + nas-path = "/media/nas/media-stack"; + renderGid = toString config.users.groups.render.gid; + videoGid = toString config.users.groups.video.gid; + inputGid = toString config.users.groups.input.gid; + intro-skipper-fix = pkgs.writeText "intro-skipper-fix" '' + #!/bin/bash + chown abc /usr/share/jellyfin/web/index.html + ''; +in +{ + options.ataraxia.containers.media-stack = { + jellyfin = mkEnableOption "Enable jellyfin container"; + }; + + config = mkIf cfg.jellyfin { + virtualisation.oci-containers.containers.jellyfin = { + autoStart = true; + # Tags: 10.10.7, version-10.10.7ubu2404, 10.10.7ubu2404-ls68 + image = "docker.io/linuxserver/jellyfin@sha256:d325675bce77eda246f13d0aa2bf94002d4e426e6e1783594cf9b6df164fcb23"; + environment = { + PUID = "1000"; + PGID = "100"; + UMASK = "002"; + TZ = "Europe/Moscow"; + http_proxy = "http://10.10.10.6:8888"; + https_proxy = "http://10.10.10.6:8888"; + }; + extraOptions = [ + "--pod=media-stack" + "--device=/dev/dri/renderD128:/dev/dri/renderD128" + "--group-add=${renderGid},${videoGid},${inputGid}" + # "--privileged" + ]; + volumes = [ + "${nas-path}/configs/jellyfin:/config" + "${nas-path}/media:/data/media" + "${intro-skipper-fix}:/custom-cont-init.d/intro-skipper-fix:ro" + ]; + }; + }; +} diff --git a/modules/nixos/containers/media-stack/kavita.nix b/modules/nixos/containers/media-stack/kavita.nix new file mode 100644 index 0000000..7324675 --- /dev/null +++ b/modules/nixos/containers/media-stack/kavita.nix @@ -0,0 +1,35 @@ +{ config, lib, ... }: +let + inherit (lib) mkEnableOption mkIf; + + cfg = config.ataraxia.containers.media-stack; + nas-path = "/media/nas/media-stack"; +in +{ + options.ataraxia.containers.media-stack = { + kavita = mkEnableOption "Enable kavita container"; + }; + + config = mkIf cfg.kavita { + virtualisation.oci-containers.containers.kavita = { + autoStart = true; + # Tags: 0.8.6, version-v0.8.6.2, v0.8.6.2-ls79 + image = "docker.io/linuxserver/kavita@sha256:b222e4b2137db2301756d018076d0bfee858077d8af24d709f1f4003d628e580"; + environment = { + PUID = "1000"; + PGID = "100"; + TZ = "Europe/Moscow"; + DOTNET_SYSTEM_GLOBALIZATION_INVARIANT = "true"; + }; + extraOptions = [ "--pod=media-stack" ]; + volumes = [ + "${nas-path}/configs/kavita:/config" + "${nas-path}/media/books:/data/books" + "${nas-path}/media/comics:/data/comics" + "${nas-path}/media/fanfics:/data/fanfics" + "${nas-path}/media/manga:/data/manga" + "${nas-path}/media/novels:/data/novels" + ]; + }; + }; +} diff --git a/modules/nixos/containers/media-stack/lidarr.nix b/modules/nixos/containers/media-stack/lidarr.nix new file mode 100644 index 0000000..d7a3a65 --- /dev/null +++ b/modules/nixos/containers/media-stack/lidarr.nix @@ -0,0 +1,32 @@ +{ config, lib, ... }: +let + inherit (lib) mkEnableOption mkIf; + + cfg = config.ataraxia.containers.media-stack; + nas-path = "/media/nas/media-stack"; +in +{ + options.ataraxia.containers.media-stack = { + lidarr = mkEnableOption "Enable lidarr container"; + }; + + config = mkIf cfg.lidarr { + virtualisation.oci-containers.containers.lidarr = { + autoStart = true; + environment = { + PUID = "1000"; + PGID = "100"; + TZ = "Europe/Moscow"; + }; + extraOptions = [ "--pod=media-stack" ]; + # Tags: 2.12.4, version-2.12.4.4658, 2.12.4.4658-ls45 + image = "docker.io/linuxserver/lidarr@sha256:71fe6d5702691c6ac8961b9b1042fdea1ff833a49c82c5e165346fa88999a48a"; + volumes = [ + "${nas-path}/configs/lidarr/config:/config" + "${nas-path}/configs/lidarr/custom-services.d:/custom-services.d" + "${nas-path}/configs/lidarr/custom-cont-init.d:/custom-cont-init.d" + "${nas-path}:/data" + ]; + }; + }; +} diff --git a/modules/nixos/containers/media-stack/medusa.nix b/modules/nixos/containers/media-stack/medusa.nix new file mode 100644 index 0000000..2a5e10e --- /dev/null +++ b/modules/nixos/containers/media-stack/medusa.nix @@ -0,0 +1,30 @@ +{ config, lib, ... }: +let + inherit (lib) mkEnableOption mkIf; + + cfg = config.ataraxia.containers.media-stack; + nas-path = "/media/nas/media-stack"; +in +{ + options.ataraxia.containers.media-stack = { + medusa = mkEnableOption "Enable medusa container"; + }; + + config = mkIf cfg.medusa { + virtualisation.oci-containers.containers.medusa = { + autoStart = true; + # Tags: 1.0.22, version-v1.0.22, v1.0.22-ls230 + image = "docker.io/linuxserver/medusa@sha256:89d7397b64b079050d8d20284fc692aee36a196885f57e5d9a396455d58a130d"; + environment = { + PUID = "1000"; + PGID = "100"; + TZ = "Europe/Moscow"; + }; + extraOptions = [ "--pod=media-stack" ]; + volumes = [ + "${nas-path}/configs/medusa:/config" + "${nas-path}:/data" + ]; + }; + }; +} diff --git a/modules/nixos/containers/media-stack/qbittorrent.nix b/modules/nixos/containers/media-stack/qbittorrent.nix new file mode 100644 index 0000000..e520962 --- /dev/null +++ b/modules/nixos/containers/media-stack/qbittorrent.nix @@ -0,0 +1,59 @@ +{ + config, + lib, + pkgs, + ... +}: +let + inherit (lib) mkEnableOption mkIf; + + cfg = config.ataraxia.containers.media-stack; + + backend = "podman"; + nas-path = "/media/nas/media-stack"; + volume = "local-nfs"; + nfs-share = "10.10.10.11:/"; +in +{ + options.ataraxia.containers.media-stack = { + qbittorrent = mkEnableOption "Enable qbittorrent container"; + }; + + config = mkIf cfg.qbittorrent { + virtualisation.oci-containers.containers.qbittorrent = { + autoStart = true; + # Tags: 5.1.2, version-5.1.2-r0, 5.1.2-r0-ls402 + image = "docker.io/linuxserver/qbittorrent@sha256:94c8c82291c4fcf86084a6efb9f806786296fad48739e4723dc9a5393073a2ae"; + environment = { + PUID = "1000"; + PGID = "100"; + UMASK = "002"; + TZ = "Europe/Moscow"; + TORRENTING_PORT = "7000"; + DOCKER_MODS = "ghcr.io/gabe565/linuxserver-mod-vuetorrent"; + }; + extraOptions = [ "--pod=media-stack" ]; + volumes = [ + "${nas-path}/configs/qbittorrent:/config" + "${nas-path}:/data" + "${volume}:/nfs" + ]; + }; + + systemd.services."podman-create-volume-${volume}" = + let + start = pkgs.writeShellScript "create-volume-${volume}" '' + podman volume exists ${volume} || podman volume create --opt type=nfs4 --opt o=rw --opt device=${nfs-share} ${volume} + ''; + in + rec { + path = [ config.virtualisation.podman.package ]; + before = [ "${backend}-qbittorrent.service" ]; + requiredBy = before; + serviceConfig = { + Type = "oneshot"; + ExecStart = start; + }; + }; + }; +} diff --git a/modules/nixos/containers/media-stack/radarr.nix b/modules/nixos/containers/media-stack/radarr.nix new file mode 100644 index 0000000..91bda02 --- /dev/null +++ b/modules/nixos/containers/media-stack/radarr.nix @@ -0,0 +1,31 @@ +{ config, lib, ... }: +let + inherit (lib) mkEnableOption mkIf; + + cfg = config.ataraxia.containers.media-stack; + nas-path = "/media/nas/media-stack"; +in +{ + options.ataraxia.containers.media-stack = { + radarr = mkEnableOption "Enable radarr container"; + }; + + config = mkIf cfg.radarr { + virtualisation.oci-containers.containers.radarr = { + autoStart = true; + environment = { + PUID = "1000"; + PGID = "100"; + UMASK = "002"; + TZ = "Europe/Moscow"; + }; + extraOptions = [ "--pod=media-stack" ]; + # Tags: 5.26.2, version-5.26.2.10099, 5.26.2.10099-ls276 + image = "docker.io/linuxserver/radarr@sha256:07a474b61394553e047ad43a1a78c1047fc99be0144c509dd91e3877f402ebcb"; + volumes = [ + "${nas-path}/configs/radarr:/config" + "${nas-path}:/data" + ]; + }; + }; +} diff --git a/modules/nixos/containers/media-stack/recyclarr.nix b/modules/nixos/containers/media-stack/recyclarr.nix new file mode 100644 index 0000000..7ac0ea2 --- /dev/null +++ b/modules/nixos/containers/media-stack/recyclarr.nix @@ -0,0 +1,29 @@ +{ config, lib, ... }: +let + inherit (lib) mkEnableOption mkIf; + + cfg = config.ataraxia.containers.media-stack; + nas-path = "/media/nas/media-stack"; +in +{ + options.ataraxia.containers.media-stack = { + recyclarr = mkEnableOption "Enable recyclarr container"; + }; + + config = mkIf cfg.recyclarr { + virtualisation.oci-containers.containers.recyclarr = { + autoStart = true; + environment = { + CRON_SCHEDULE = "@daily"; + TZ = "Europe/Moscow"; + }; + extraOptions = [ "--pod=media-stack" ]; + # Tags: 7.4.1, 7.4, 7 + image = "ghcr.io/recyclarr/recyclarr@sha256:759540877f95453eca8a26c1a93593e783a7a824c324fbd57523deffb67f48e1"; + volumes = [ + "${nas-path}/configs/recyclarr:/config" + ]; + user = "1000:100"; + }; + }; +} diff --git a/modules/nixos/containers/media-stack/sonarr.nix b/modules/nixos/containers/media-stack/sonarr.nix new file mode 100644 index 0000000..e8bc8fc --- /dev/null +++ b/modules/nixos/containers/media-stack/sonarr.nix @@ -0,0 +1,31 @@ +{ config, lib, ... }: +let + inherit (lib) mkEnableOption mkIf; + + cfg = config.ataraxia.containers.media-stack; + nas-path = "/media/nas/media-stack"; +in +{ + options.ataraxia.containers.media-stack = { + sonarr = mkEnableOption "Enable sonarr container"; + }; + + config = mkIf cfg.sonarr { + virtualisation.oci-containers.containers.sonarr = { + autoStart = true; + environment = { + PUID = "1000"; + PGID = "100"; + UMASK = "002"; + TZ = "Europe/Moscow"; + }; + extraOptions = [ "--pod=media-stack" ]; + # Tags: 4.0.15, version-4.0.15.2941, 4.0.15.2941-ls285 + image = "docker.io/linuxserver/sonarr@sha256:1156329d544b38bd1483add75c9b72c559f20e1ca043fd2d6376c2589d38951f"; + volumes = [ + "${nas-path}/configs/sonarr:/config" + "${nas-path}:/data" + ]; + }; + }; +}