diff --git a/nixos/doc/manual/from_md/release-notes/rl-2305.section.xml b/nixos/doc/manual/from_md/release-notes/rl-2305.section.xml
index c48907b8702..b4ff52767f6 100644
--- a/nixos/doc/manual/from_md/release-notes/rl-2305.section.xml
+++ b/nixos/doc/manual/from_md/release-notes/rl-2305.section.xml
@@ -68,6 +68,13 @@
programs.fzf.
+
+
+ gmediarender,
+ a simple, headless UPnP/DLNA renderer. Available as
+ services.gmediarender.
+
+
stevenblack-blocklist,
diff --git a/nixos/doc/manual/release-notes/rl-2305.section.md b/nixos/doc/manual/release-notes/rl-2305.section.md
index 07f6d729b64..803e9eb6e43 100644
--- a/nixos/doc/manual/release-notes/rl-2305.section.md
+++ b/nixos/doc/manual/release-notes/rl-2305.section.md
@@ -26,6 +26,8 @@ In addition to numerous new and upgraded packages, this release has the followin
- [fzf](https://github.com/junegunn/fzf), a command line fuzzyfinder. Available as [programs.fzf](#opt-programs.fzf.fuzzyCompletion).
+- [gmediarender](https://github.com/hzeller/gmrender-resurrect), a simple, headless UPnP/DLNA renderer. Available as [services.gmediarender](options.html#opt-services.gmediarender.enable).
+
- [stevenblack-blocklist](https://github.com/StevenBlack/hosts), A unified hosts file with base extensions for blocking unwanted websites. Available as [networking.stevenblack](options.html#opt-networking.stevenblack.enable).
- [atuin](https://github.com/ellie/atuin), a sync server for shell history. Available as [services.atuin](#opt-services.atuin.enable).
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index 0d98752e201..dce6e878540 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -295,6 +295,7 @@
./services/amqp/rabbitmq.nix
./services/audio/alsa.nix
./services/audio/botamusique.nix
+ ./services/audio/gmediarender.nix
./services/audio/hqplayerd.nix
./services/audio/icecast.nix
./services/audio/jack.nix
diff --git a/nixos/modules/services/audio/gmediarender.nix b/nixos/modules/services/audio/gmediarender.nix
new file mode 100644
index 00000000000..2f23232d19c
--- /dev/null
+++ b/nixos/modules/services/audio/gmediarender.nix
@@ -0,0 +1,116 @@
+{ pkgs, lib, config, utils, ... }:
+
+with lib;
+
+let
+ cfg = config.services.gmediarender;
+in
+{
+ options.services.gmediarender = {
+ enable = mkEnableOption (mdDoc "the gmediarender DLNA renderer");
+
+ audioDevice = mkOption {
+ type = types.nullOr types.str;
+ default = null;
+ description = mdDoc ''
+ The audio device to use.
+ '';
+ };
+
+ audioSink = mkOption {
+ type = types.nullOr types.str;
+ default = null;
+ description = mdDoc ''
+ The audio sink to use.
+ '';
+ };
+
+ friendlyName = mkOption {
+ type = types.nullOr types.str;
+ default = null;
+ description = mdDoc ''
+ A "friendly name" for identifying the endpoint.
+ '';
+ };
+
+ initialVolume = mkOption {
+ type = types.nullOr types.int;
+ default = 0;
+ description = mdDoc ''
+ A default volume attenuation (in dB) for the endpoint.
+ '';
+ };
+
+ package = mkPackageOptionMD pkgs "gmediarender" {
+ default = "gmrender-resurrect";
+ };
+
+ port = mkOption {
+ type = types.nullOr types.port;
+ default = null;
+ description = mdDoc "Port that will be used to accept client connections.";
+ };
+
+ uuid = mkOption {
+ type = types.nullOr types.str;
+ default = null;
+ description = mdDoc ''
+ A UUID for uniquely identifying the endpoint. If you have
+ multiple renderers on your network, you MUST set this.
+ '';
+ };
+ };
+
+ config = mkIf cfg.enable {
+ systemd = {
+ services.gmediarender = {
+ after = [ "network-online.target" ];
+ wantedBy = [ "multi-user.target" ];
+ description = "gmediarender server daemon";
+ environment = {
+ XDG_CACHE_HOME = "%t/gmediarender";
+ };
+ serviceConfig = {
+ DynamicUser = true;
+ User = "gmediarender";
+ Group = "gmediarender";
+ SupplementaryGroups = [ "audio" ];
+ ExecStart =
+ "${cfg.package}/bin/gmediarender " +
+ optionalString (cfg.audioDevice != null) ("--gstout-audiodevice=${utils.escapeSystemdExecArg cfg.audioDevice} ") +
+ optionalString (cfg.audioSink != null) ("--gstout-audiosink=${utils.escapeSystemdExecArg cfg.audioSink} ") +
+ optionalString (cfg.friendlyName != null) ("--friendly-name=${utils.escapeSystemdExecArg cfg.friendlyName} ") +
+ optionalString (cfg.initialVolume != 0) ("--initial-volume=${toString cfg.initialVolume} ") +
+ optionalString (cfg.port != null) ("--port=${toString cfg.port} ") +
+ optionalString (cfg.uuid != null) ("--uuid=${utils.escapeSystemdExecArg cfg.uuid} ");
+ Restart = "always";
+ RuntimeDirectory = "gmediarender";
+
+ # Security options:
+ CapabilityBoundingSet = "";
+ LockPersonality = true;
+ MemoryDenyWriteExecute = true;
+ NoNewPrivileges = true;
+ # PrivateDevices = true;
+ PrivateTmp = true;
+ PrivateUsers = true;
+ ProcSubset = "pid";
+ ProtectClock = true;
+ ProtectControlGroups = true;
+ ProtectHome = true;
+ ProtectHostname = true;
+ ProtectKernelLogs = true;
+ ProtectKernelModules = true;
+ ProtectKernelTunables = true;
+ ProtectProc = "invisible";
+ RestrictNamespaces = true;
+ RestrictRealtime = true;
+ RestrictSUIDSGID = true;
+ SystemCallArchitectures = "native";
+ SystemCallFilter = [ "@system-service" "~@privileged" ];
+ UMask = 066;
+ };
+ };
+ };
+ };
+}