From 07a97f360c3177bf518d900bada88fbd1d7f2060 Mon Sep 17 00:00:00 2001 From: Tibo De Peuter Date: Thu, 11 Sep 2025 11:27:37 +0200 Subject: [PATCH 1/5] chore(arr): Update images --- modules/apps/arr/default.nix | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/apps/arr/default.nix b/modules/apps/arr/default.nix index 5cff39c..3b05429 100644 --- a/modules/apps/arr/default.nix +++ b/modules/apps/arr/default.nix @@ -243,7 +243,7 @@ in { port = 6767; in lib.mkIf cfg.bazarr.enable { hostname = "bazarr"; - image = "ghcr.io/hotio/bazarr:release-1.4.4"; + image = "ghcr.io/hotio/bazarr:release-1.5.2"; autoStart = true; ports = lib.mkIf cfg.bazarr.exposePorts [ "${toString port}:${toString port}/tcp" @@ -279,7 +279,7 @@ in { port = 9696; in lib.mkIf cfg.prowlarr.enable { hostname = "prowlarr"; - image = "ghcr.io/hotio/prowlarr:release-1.23.1.4708"; + image = "ghcr.io/hotio/prowlarr:release-2.0.5.5160"; autoStart = true; ports = lib.mkIf cfg.prowlarr.exposePorts [ "${toString port}:${toString port}/tcp" @@ -310,7 +310,7 @@ in { port = 10095; in lib.mkIf cfg.qbittorrent.enable { hostname = "qbittorrent"; - image = "ghcr.io/hotio/qbittorrent:release-4.6.7"; + image = "ghcr.io/hotio/qbittorrent:release-5.1.2"; autoStart = true; ports = lib.mkIf cfg.qbittorrent.exposePorts [ "${toString port}:${toString port}/tcp" @@ -343,7 +343,7 @@ in { port = 7878; in lib.mkIf cfg.radarr.enable { hostname = "radarr"; - image = "ghcr.io/hotio/radarr:release-5.9.1.9070"; + image = "ghcr.io/hotio/radarr:release-5.28.0.10205"; autoStart = true; ports = lib.mkIf cfg.radarr.exposePorts [ "${toString port}:${toString port}/tcp" @@ -377,7 +377,7 @@ in { port = 8989; in lib.mkIf cfg.sonarr.enable { hostname = "sonarr"; - image = "ghcr.io/hotio/sonarr:release-4.0.9.2244"; + image = "ghcr.io/hotio/sonarr:release-4.0.15.2941"; autoStart = true; ports = lib.mkIf cfg.sonarr.exposePorts [ "${toString port}:${toString port}/tcp" From cfee4fd83541457971bbbf93b38c001f725bab01 Mon Sep 17 00:00:00 2001 From: Tibo De Peuter Date: Thu, 11 Sep 2025 12:09:26 +0200 Subject: [PATCH 2/5] fix(calibre): Specify proxy network --- hosts/Production/default.nix | 5 ++++- modules/apps/calibre/default.nix | 5 +++++ modules/apps/default.nix | 1 + 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/hosts/Production/default.nix b/hosts/Production/default.nix index cd929ff..9bb565d 100644 --- a/hosts/Production/default.nix +++ b/hosts/Production/default.nix @@ -3,7 +3,10 @@ { config = { homelab = { - apps.changedetection.enable = true; + apps = { + calibre.enable = true; + traefik.enable = true; + }; virtualisation.guest.enable = true; }; diff --git a/modules/apps/calibre/default.nix b/modules/apps/calibre/default.nix index aa00c89..e18ecb9 100644 --- a/modules/apps/calibre/default.nix +++ b/modules/apps/calibre/default.nix @@ -11,6 +11,7 @@ let calibre-web-config = "/srv/calibre-web-config"; networkName = "calibre"; + proxyNet = config.homelab.apps.traefik.sharedNetworkName; in { options.homelab.apps.calibre = { enable = lib.mkEnableOption "Calibre (Desktop + Web)"; @@ -102,6 +103,7 @@ in { ]; extraOptions = [ "--network=${networkName}" + "--network=${proxyNet}" # syscalls are unkown to Docker #"--security-opt" "seccomp=unconfined" @@ -122,6 +124,7 @@ in { ]; labels = { "traefik.enable" = "true"; + "traefik.docker.network" = proxyNet; "traefik.http.routers.calibre.rule" = "Host(`calibre.depeuter.dev`)"; "traefik.http.services.calibre.loadbalancer.server.port" = toString innerPort; }; @@ -156,6 +159,7 @@ in { ]; extraOptions = [ "--network=${networkName}" + "--network=${proxyNet}" ]; environment = { inherit PUID PGID; @@ -175,6 +179,7 @@ in { ]; labels = { "traefik.enable" = "true"; + "traefik.docker.network" = proxyNet; "traefik.http.routers.calibre-web.rule" = "Host(`books.depeuter.dev`)"; "traefik.http.services.calibre-web.loadbalancer.server.port" = toString innerPort; }; diff --git a/modules/apps/default.nix b/modules/apps/default.nix index 81c6a06..7c8b8f8 100644 --- a/modules/apps/default.nix +++ b/modules/apps/default.nix @@ -10,6 +10,7 @@ ./plex ./speedtest ./technitium-dns + ./traefik ./vaultwarden ]; } From 59f721f4d7d3d50fbfe68312ce19b820658930ad Mon Sep 17 00:00:00 2001 From: Tibo De Peuter Date: Thu, 11 Sep 2025 12:18:10 +0200 Subject: [PATCH 3/5] chore(calibre): Update images --- modules/apps/calibre/default.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/apps/calibre/default.nix b/modules/apps/calibre/default.nix index e18ecb9..bddf5c8 100644 --- a/modules/apps/calibre/default.nix +++ b/modules/apps/calibre/default.nix @@ -93,7 +93,7 @@ in { innerPort = 8080; in { hostname = "calibre"; - image = "lscr.io/linuxserver/calibre:8.5.0"; + image = "lscr.io/linuxserver/calibre:v8.10.0-ls354"; autoStart = true; ports = [ # Open ports if you don't use Traefik @@ -151,7 +151,7 @@ in { innerPort = 8083; in { hostname = "calibre-web"; - image = "lscr.io/linuxserver/calibre-web:0.6.24"; + image = "lscr.io/linuxserver/calibre-web:0.6.25-ls346"; autoStart = true; ports = [ # Open ports if you don't use Traefik From bdb4ad8160557fa71321f30c995495216fc17ce0 Mon Sep 17 00:00:00 2001 From: Tibo De Peuter Date: Wed, 1 Oct 2025 16:33:55 +0200 Subject: [PATCH 4/5] Updates --- modules/apps/arr/default.nix | 2 +- modules/apps/jellyfin/default.nix | 24 +++++++++--------------- modules/services/actions/default.nix | 2 +- 3 files changed, 11 insertions(+), 17 deletions(-) diff --git a/modules/apps/arr/default.nix b/modules/apps/arr/default.nix index 3b05429..e2c0df5 100644 --- a/modules/apps/arr/default.nix +++ b/modules/apps/arr/default.nix @@ -343,7 +343,7 @@ in { port = 7878; in lib.mkIf cfg.radarr.enable { hostname = "radarr"; - image = "ghcr.io/hotio/radarr:release-5.28.0.10205"; + image = "ghcr.io/hotio/radarr:testing-5.28.0.10205"; autoStart = true; ports = lib.mkIf cfg.radarr.exposePorts [ "${toString port}:${toString port}/tcp" diff --git a/modules/apps/jellyfin/default.nix b/modules/apps/jellyfin/default.nix index 5b4081a..011f56b 100644 --- a/modules/apps/jellyfin/default.nix +++ b/modules/apps/jellyfin/default.nix @@ -4,6 +4,7 @@ let cfg = config.homelab.apps.jellyfin; networkName = "jellyfin"; + inherit (config.homelab.fileSystems) media; UID = 3008; GID = config.users.groups.media.gid; @@ -12,6 +13,11 @@ in { config = lib.mkIf cfg.enable { homelab = { + fileSystems.media.video = { + enable = true; + permissions = [ "read" ]; + }; + users = { apps.enable = true; media.enable = true; @@ -32,18 +38,6 @@ in { ]; }; - "/srv/video" = { - device = "192.168.0.11:/mnt/SMALL/MEDIA/VIDEO"; - fsType = "nfs"; - options = [ - "ro" - "nfsvers=4.2" - "async" "soft" - "timeo=100" "retry=50" "actimeo=1800" "lookupcache=all" - "nosuid" "tcp" - ]; - }; - "/srv/homevideo" = { device = "192.168.0.11:/mnt/BIG/MEDIA/HOMEVIDEO/ARCHIVE"; fsType = "nfs"; @@ -101,7 +95,7 @@ in { virtualisation.oci-containers.containers = { jellyfin = { hostname = "jellyfin"; - image = "jellyfin/jellyfin:10.10.0"; + image = "jellyfin/jellyfin:10.10.7"; user = "${toString UID}:${toString GID}"; autoStart = true; ports = [ @@ -117,7 +111,7 @@ in { "cache:/cache" "/srv/audio:/media/audio" - "/srv/video:/media/video" + "${media.video.hostPath}:/media/video" "/srv/homevideo:/media/homevideo" "/srv/photo:/media/photo" ]; @@ -144,7 +138,7 @@ in { feishinPort = "9180"; in { hostname = "feishin"; - image = "ghcr.io/jeffvli/feishin:0.7.1"; + image = "ghcr.io/jeffvli/feishin:0.19.0"; autoStart = true; ports = [ "${feishinPort}:9180/tcp" # Web player (HTTP) diff --git a/modules/services/actions/default.nix b/modules/services/actions/default.nix index 338b963..ea6b025 100644 --- a/modules/services/actions/default.nix +++ b/modules/services/actions/default.nix @@ -44,6 +44,6 @@ in { ]; }; }; - }; } + From da3157c8188b08a6b4d4147ea2e3138d4a71608c Mon Sep 17 00:00:00 2001 From: Tibo De Peuter Date: Wed, 1 Oct 2025 16:34:55 +0200 Subject: [PATCH 5/5] feat(penpot): Add module --- modules/apps/penpot/default.nix | 220 ++++++++++++++++++++++++++++++++ 1 file changed, 220 insertions(+) create mode 100644 modules/apps/penpot/default.nix diff --git a/modules/apps/penpot/default.nix b/modules/apps/penpot/default.nix new file mode 100644 index 0000000..0ec01cd --- /dev/null +++ b/modules/apps/penpot/default.nix @@ -0,0 +1,220 @@ +{ config, lib, pkgs, ... }: + +let + cfg = config.homelab.apps.penpot; + + networkName = "penpot"; + UID = config.users.users.apps.uid; + GID = config.users.groups.apps.gid; + + version = "2.5.4"; + + srvPath = "/srv/penpot"; + assetsPath = "/opt/data/assets"; + + PENPOT_FLAGS = "enable-smtp enable-prepl-server disable-secure-session-cookies disable-onboarding disable-registration"; + # Max body size (30MiB); Used for plain requests, should never be + # greater than multi-part size + PENPOT_HTTP_SERVER_MAX_BODY_SIZE = "31457280"; + # Max multipart body size (350MiB) + PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE = "367001600"; + + dbName = "penpot"; + dbUser = "penpot"; + dbPass = "penpot"; + + docker = config.virtualisation.oci-containers.containers; +in { + options.homelab.apps.penpot.enable = lib.mkEnableOption "Penpot using Docker"; + + config = lib.mkIf cfg.enable { + homelab = { + users.apps.enable = true; + virtualisation.containers.enable = true; + }; + + fileSystems."${srvPath}" = { + device = "192.168.0.11:/mnt/SMALL/CONFIG/PENPOT"; + fsType = "nfs"; + options = [ + "rw" + "nfsvers=4.2" + "sync" "hard" "timeo=600" + "retrans=2" + "_netdev" + "nosuid" "tcp" + ]; + }; + + # Make sure the Docker network exists. + systemd.services."docker-${networkName}-create-network" = lib.mkIf cfg.enable { + description = "Create Docker network for ${networkName}"; + requiredBy = [ + "docker-penpot-frontend.service" + "docker-penpot-backend.service" + "docker-penpot-exporter.service" + "docker-penpot-postgres.service" + "docker-penpot-redis.service" + ]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + }; + script = '' + if ! ${pkgs.docker}/bin/docker network ls | grep -q ${networkName}; then + ${pkgs.docker}/bin/docker network create ${networkName} + fi + ''; + }; + + virtualisation.oci-containers.containers = let + frontendPort = 8080; + redisUri = "redis://${docker.penpot-redis.hostname}/0"; + in { + penpot-frontend = { + hostname = "penpot-frontend"; + image = "penpotapp/frontend:${version}"; + # user = "${toString UID}:${toString GID}"; + user = "0:${toString GID}"; + ports = [ + # "9001:${toString frontendPort}/tcp" + ]; + extraOptions = [ + "--network=${networkName}" + ]; + environment = { + inherit PENPOT_FLAGS; + inherit PENPOT_HTTP_SERVER_MAX_BODY_SIZE; + inherit PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE; + }; + volumes = [ + "${srvPath}:${assetsPath}" + ]; + dependsOn = [ + docker.penpot-backend.hostname + docker.penpot-exporter.hostname + ]; + labels = { + "traefik.enable" = "true"; + "traefik.http.routers.penpot.rule" = "Host(`penpot.depeuter.dev`)"; + "traefik.http.services.penpot.loadbalancer.server.port" = toString frontendPort; + "traefik.tls.options.default.minVersion" = "VersionTLS13"; + }; + autoStart = true; + }; + penpot-backend = { + hostname = "penpot-backend"; + image = "penpotapp/backend:latest"; + # user = "${toString UID}:${toString GID}"; + user = "0:${toString GID}"; + extraOptions = [ + "--network=${networkName}" + ]; + environmentFiles = [ + /home/admin/.penpot.secret + ]; + environment = { + inherit PENPOT_FLAGS; + inherit PENPOT_HTTP_SERVER_MAX_BODY_SIZE; + inherit PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE; + + PENPOT_PUBLIC_URI = "https://penpot.depeuter.dev"; + + ## Database connection parameters. Don't touch them unless you are using custom + ## postgresql connection parameters. + + PENPOT_DATABASE_URI = "postgresql://${docker.penpot-postgres.hostname}/${dbName}"; + PENPOT_DATABASE_USERNAME = dbUser; + PENPOT_DATABASE_PASSWORD = dbPass; + + ## Redis is used for the websockets notifications. Don't touch unless the redis + ## container has different parameters or different name. + + PENPOT_REDIS_URI = redisUri; + + ## Default configuration for assets storage: using filesystem based with all files + ## stored in a docker volume. + + PENPOT_ASSETS_STORAGE_BACKEND = "assets-fs"; + PENPOT_STORAGE_ASSETS_FS_DIRECTORY = assetsPath; + + ## Telemetry. When enabled, a periodical process will send anonymous data about this + ## instance. Telemetry data will enable us to learn how the application is used, + ## based on real scenarios. If you want to help us, please leave it enabled. You can + ## audit what data we send with the code available on github. + + PENPOT_TELEMETRY_ENABLED = "false"; + PENPOT_TELEMETRY_REFERER = "compose"; + + # PENPOT_SMTP_HOST = "smtp.gmail.com"; + # PENPOT_SMTP_PORT = "465"; + # PENPOT_SMTP_USERNAME: "kmtl.hugo@gmail.com"; + # PENPOT_SMTP_PASSWORD: + # PENPOT_SMTP_TLS: true + }; + volumes = [ + "${srvPath}:${assetsPath}" + ]; + dependsOn = [ + docker.penpot-postgres.hostname + docker.penpot-redis.hostname + ]; + autoStart = true; + }; + penpot-exporter = { + hostname = "penpot-exporter"; + image = "penpotapp/exporter:latest"; + extraOptions = [ + "--network=${networkName}" + ]; + environment = { + # Don't touch it; this uses an internal docker network to + # communicate with the frontend. + PENPOT_PUBLIC_URI = "http://${docker.penpot-frontend.hostname}:${toString frontendPort}"; + + ## Redis is used for the websockets notifications. + PENPOT_REDIS_URI = redisUri; + }; + dependsOn = [ + docker.penpot-redis.hostname + ]; + autoStart = true; + }; + penpot-postgres = { + hostname = "penpot-postgres"; + image = "postgres:15"; + extraOptions = [ + "--network=${networkName}" + "--health-cmd='pg_isready -U ${dbUser}'" + "--health-interval=2s" + "--health-retries=5" + "--health-timeout=10s" + "--health-start-period=2s" + ]; + environment = { + POSTGRES_INITDB_ARGS = "--data-checksums"; + POSTGRES_DB = dbName; + POSTGRES_USER = dbUser; + POSTGRES_PASSWORD = dbPass; + }; + volumes = [ + "penpot_postgres_v15:/var/lib/postgresql/data" + ]; + autoStart = true; + }; + penpot-redis = { + hostname = "penpot-redis"; + image = "redis:7.2"; + extraOptions = [ + "--network=${networkName}" + "--health-cmd='redis-cli ping | grep PONG'" + "--health-interval=1s" + "--health-retries=5" + "--health-timeout=3s" + "--health-start-period=3s" + ]; + autoStart = true; + }; + }; + }; +}