feat: migrate media-stack containers to quadlet

This commit is contained in:
Dmitriy Kholkin 2025-07-10 18:39:31 +03:00
parent ce43a4f211
commit d22c2abb30
Signed by: AtaraxiaDev
GPG Key ID: FD266B810DF48DF2
11 changed files with 258 additions and 201 deletions

View File

@ -8,6 +8,8 @@ let
inherit (lib) mkEnableOption mkIf;
cfg = config.ataraxia.containers.media-stack;
pods = config.virtualisation.quadlet.pods;
nas-path = "/media/nas/media-stack";
caddyconf = pkgs.writeText "Caddyfile" ''
@ -47,21 +49,23 @@ in
};
config = mkIf cfg.caddy {
virtualisation.oci-containers.containers.media-caddy = {
virtualisation.quadlet.containers.caddy = {
autoStart = true;
containerConfig = {
# Tags: release-20b7f25, release-2.10.0, release
image = "ghcr.io/hotio/caddy@sha256:937fe02672e7ce7f189e28d45c4ccfe86b2a7d5791b4e04badb55e143e32d5b7";
environment = {
pod = pods.media-stack.ref;
environments = {
PUID = "1000";
PGID = "100";
UMASK = "002";
TZ = "Europe/Moscow";
};
extraOptions = [ "--pod=media-stack" ];
volumes = [
"${nas-path}/configs/caddy:/config"
"${caddyconf}:/config/Caddyfile"
];
};
};
};
}

View File

@ -1,7 +1,6 @@
{
config,
lib,
pkgs,
...
}:
let
@ -9,16 +8,22 @@ let
mkDefault
mkEnableOption
mkIf
optionals
mkOption
recursiveUpdate
;
inherit (lib.types) bool;
cfg = config.ataraxia.containers.media-stack;
networks = config.virtualisation.quadlet.networks;
nginx = config.ataraxia.services.nginx;
backend = config.virtualisation.oci-containers.backend;
pod-name = "media-stack";
caddy-port = "8180";
# TODO: fix caddy for medusa. Maybe change to something else
medusa-port = "8180";
open-ports = [
# caddy
"127.0.0.1:8180:8180"
"127.0.0.1:${caddy-port}:${caddy-port}"
"127.0.0.1:${medusa-port}:${medusa-port}"
# qbittorrent
"0.0.0.0:7000:7000"
"0.0.0.0:7000:7000/udp"
@ -41,6 +46,11 @@ in
options.ataraxia.containers.media-stack = {
enable = mkEnableOption "Enable media-stack containers";
nginxHost = mkOption {
type = bool;
default = config.ataraxia.services.nginx.enable;
description = "Enable nginx vHost integration";
};
};
config = mkIf cfg.enable {
@ -55,39 +65,58 @@ in
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
virtualisation.quadlet.pods.media-stack = {
podConfig = {
dns = [ pod-dns ];
networks = [ networks.br-services.ref ];
publishPorts = open-ports;
};
};
services.nginx.virtualHosts = mkIf cfg.nginxHost {
"media-stack" = recursiveUpdate nginx.defaultSettings {
serverAliases = [
"jackett.ataraxiadev.com"
"kavita.ataraxiadev.com"
"lidarr.ataraxiadev.com"
"qbit.ataraxiadev.com"
"radarr.ataraxiadev.com"
"sonarr.ataraxiadev.com"
];
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;
locations."/" = {
proxyPass = "http://127.0.0.1:${caddy-port}";
proxyWebsockets = true;
extraConfig = ''
proxy_buffer_size 128k;
proxy_buffers 4 256k;
proxy_busy_buffers_size 256k;
send_timeout 15m;
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 15m;
'';
};
};
"jellyfin.ataraxiadev.com" = recursiveUpdate nginx.defaultSettings {
locations."/" = {
proxyPass = "http://127.0.0.1:${caddy-port}";
extraConfig = ''
proxy_buffering off;
'';
};
locations."/socket" = {
proxyPass = "http://127.0.0.1:${caddy-port}";
proxyWebsockets = true;
};
extraConfig = ''
client_max_body_size 50M;
'';
};
"medusa.ataraxiadev.com" = recursiveUpdate nginx.defaultSettings {
locations."/" = {
proxyPass = "http://127.0.0.1:${medusa-port}";
proxyWebsockets = true;
};
};
};
};

View File

@ -3,6 +3,8 @@ let
inherit (lib) mkEnableOption mkIf;
cfg = config.ataraxia.containers.media-stack;
pods = config.virtualisation.quadlet.pods;
nas-path = "/media/nas/media-stack";
in
{
@ -11,20 +13,22 @@ in
};
config = mkIf cfg.jackett {
virtualisation.oci-containers.containers.jackett = {
virtualisation.quadlet.containers.jackett = {
autoStart = true;
environment = {
containerConfig = {
# Tags: 0.22.2117, version-v0.22.2117, v0.22.2117-ls80
image = "docker.io/linuxserver/jackett@sha256:221606b0ed7df0d66e601d0ba83f5f9cc9b9c761bafad3507d6854406b3a447b";
pod = pods.media-stack.ref;
environments = {
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

@ -8,6 +8,8 @@ let
inherit (lib) mkEnableOption mkIf;
cfg = config.ataraxia.containers.media-stack;
pods = config.virtualisation.quadlet.pods;
nas-path = "/media/nas/media-stack";
renderGid = toString config.users.groups.render.gid;
videoGid = toString config.users.groups.video.gid;
@ -23,11 +25,13 @@ in
};
config = mkIf cfg.jellyfin {
virtualisation.oci-containers.containers.jellyfin = {
virtualisation.quadlet.containers.jellyfin = {
autoStart = true;
containerConfig = {
# Tags: 10.10.7, version-10.10.7ubu2404, 10.10.7ubu2404-ls68
image = "docker.io/linuxserver/jellyfin@sha256:d325675bce77eda246f13d0aa2bf94002d4e426e6e1783594cf9b6df164fcb23";
environment = {
pod = pods.media-stack.ref;
environments = {
PUID = "1000";
PGID = "100";
UMASK = "002";
@ -35,12 +39,13 @@ in
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"
addGroups = [
renderGid
videoGid
inputGid
];
devices = [ "/dev/dri/renderD128" ];
# podmanArgs = [ "--privileged" ];
volumes = [
"${nas-path}/configs/jellyfin:/config"
"${nas-path}/media:/data/media"
@ -48,4 +53,5 @@ in
];
};
};
};
}

View File

@ -3,6 +3,8 @@ let
inherit (lib) mkEnableOption mkIf;
cfg = config.ataraxia.containers.media-stack;
pods = config.virtualisation.quadlet.pods;
nas-path = "/media/nas/media-stack";
in
{
@ -11,17 +13,18 @@ in
};
config = mkIf cfg.kavita {
virtualisation.oci-containers.containers.kavita = {
virtualisation.quadlet.containers.kavita = {
autoStart = true;
containerConfig = {
# Tags: 0.8.6, version-v0.8.6.2, v0.8.6.2-ls79
image = "docker.io/linuxserver/kavita@sha256:b222e4b2137db2301756d018076d0bfee858077d8af24d709f1f4003d628e580";
environment = {
pod = pods.media-stack.ref;
environments = {
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"
@ -32,4 +35,5 @@ in
];
};
};
};
}

View File

@ -3,6 +3,8 @@ let
inherit (lib) mkEnableOption mkIf;
cfg = config.ataraxia.containers.media-stack;
pods = config.virtualisation.quadlet.pods;
nas-path = "/media/nas/media-stack";
in
{
@ -11,16 +13,17 @@ in
};
config = mkIf cfg.lidarr {
virtualisation.oci-containers.containers.lidarr = {
virtualisation.quadlet.containers.lidarr = {
autoStart = true;
environment = {
containerConfig = {
# Tags: 2.12.4, version-2.12.4.4658, 2.12.4.4658-ls45
image = "docker.io/linuxserver/lidarr@sha256:71fe6d5702691c6ac8961b9b1042fdea1ff833a49c82c5e165346fa88999a48a";
pod = pods.media-stack.ref;
environments = {
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"
@ -29,4 +32,5 @@ in
];
};
};
};
}

View File

@ -3,6 +3,8 @@ let
inherit (lib) mkEnableOption mkIf;
cfg = config.ataraxia.containers.media-stack;
pods = config.virtualisation.quadlet.pods;
nas-path = "/media/nas/media-stack";
in
{
@ -11,20 +13,22 @@ in
};
config = mkIf cfg.medusa {
virtualisation.oci-containers.containers.medusa = {
virtualisation.quadlet.containers.medusa = {
autoStart = true;
containerConfig = {
# Tags: 1.0.22, version-v1.0.22, v1.0.22-ls230
image = "docker.io/linuxserver/medusa@sha256:89d7397b64b079050d8d20284fc692aee36a196885f57e5d9a396455d58a130d";
environment = {
pod = pods.media-stack.ref;
environments = {
PUID = "1000";
PGID = "100";
TZ = "Europe/Moscow";
};
extraOptions = [ "--pod=media-stack" ];
volumes = [
"${nas-path}/configs/medusa:/config"
"${nas-path}:/data"
];
};
};
};
}

View File

@ -1,17 +1,12 @@
{
config,
lib,
pkgs,
...
}:
{ config, lib, ... }:
let
inherit (lib) mkEnableOption mkIf;
cfg = config.ataraxia.containers.media-stack;
volumes = config.virtualisation.quadlet.volumes;
pods = config.virtualisation.quadlet.pods;
backend = "podman";
nas-path = "/media/nas/media-stack";
volume = "local-nfs";
nfs-share = "10.10.10.11:/";
in
{
@ -20,11 +15,13 @@ in
};
config = mkIf cfg.qbittorrent {
virtualisation.oci-containers.containers.qbittorrent = {
virtualisation.quadlet.containers.qbittorrent = {
autoStart = true;
containerConfig = {
# Tags: 5.1.2, version-5.1.2-r0, 5.1.2-r0-ls402
image = "docker.io/linuxserver/qbittorrent@sha256:94c8c82291c4fcf86084a6efb9f806786296fad48739e4723dc9a5393073a2ae";
environment = {
pod = pods.media-stack.ref;
environments = {
PUID = "1000";
PGID = "100";
UMASK = "002";
@ -32,27 +29,20 @@ in
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"
"${volumes.nfs-share.ref}:/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;
virtualisation.quadlet.volumes.nfs-share = {
volumeConfig = {
device = nfs-share;
type = "nfs4";
options = "rw";
};
};
};

View File

@ -3,6 +3,8 @@ let
inherit (lib) mkEnableOption mkIf;
cfg = config.ataraxia.containers.media-stack;
pods = config.virtualisation.quadlet.pods;
nas-path = "/media/nas/media-stack";
in
{
@ -11,21 +13,23 @@ in
};
config = mkIf cfg.radarr {
virtualisation.oci-containers.containers.radarr = {
virtualisation.quadlet.containers.radarr = {
autoStart = true;
environment = {
containerConfig = {
# Tags: 5.26.2, version-5.26.2.10099, 5.26.2.10099-ls276
image = "docker.io/linuxserver/radarr@sha256:07a474b61394553e047ad43a1a78c1047fc99be0144c509dd91e3877f402ebcb";
pod = pods.media-stack.ref;
environments = {
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

@ -3,6 +3,8 @@ let
inherit (lib) mkEnableOption mkIf;
cfg = config.ataraxia.containers.media-stack;
pods = config.virtualisation.quadlet.pods;
nas-path = "/media/nas/media-stack";
in
{
@ -11,19 +13,21 @@ in
};
config = mkIf cfg.recyclarr {
virtualisation.oci-containers.containers.recyclarr = {
virtualisation.quadlet.containers.recyclarr = {
autoStart = true;
environment = {
containerConfig = {
# Tags: 7.4.1, 7.4, 7
image = "ghcr.io/recyclarr/recyclarr@sha256:759540877f95453eca8a26c1a93593e783a7a824c324fbd57523deffb67f48e1";
pod = pods.media-stack.ref;
user = "1000:100";
environments = {
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

@ -3,6 +3,8 @@ let
inherit (lib) mkEnableOption mkIf;
cfg = config.ataraxia.containers.media-stack;
pods = config.virtualisation.quadlet.pods;
nas-path = "/media/nas/media-stack";
in
{
@ -11,21 +13,23 @@ in
};
config = mkIf cfg.sonarr {
virtualisation.oci-containers.containers.sonarr = {
virtualisation.quadlet.containers.sonarr = {
autoStart = true;
environment = {
containerConfig = {
# Tags: 4.0.15, version-4.0.15.2941, 4.0.15.2941-ls285
image = "docker.io/linuxserver/sonarr@sha256:1156329d544b38bd1483add75c9b72c559f20e1ca043fd2d6376c2589d38951f";
pod = pods.media-stack.ref;
environments = {
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"
];
};
};
};
}