feat: add media-stack containers

This commit is contained in:
Dmitriy Kholkin 2025-07-08 20:14:39 +03:00
parent daa99bf963
commit f52eaa8eb2
Signed by: AtaraxiaDev
GPG Key ID: FD266B810DF48DF2
12 changed files with 490 additions and 0 deletions

View File

@ -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;

View File

@ -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"
];
};
};
}

View File

@ -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;
};
};
};
}

View File

@ -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"
];
};
};
}

View File

@ -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"
];
};
};
}

View File

@ -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"
];
};
};
}

View File

@ -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"
];
};
};
}

View File

@ -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"
];
};
};
}

View File

@ -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;
};
};
};
}

View File

@ -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"
];
};
};
}

View File

@ -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";
};
};
}

View File

@ -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"
];
};
};
}