at 24.11-pre 6.5 kB view raw
1{ config, pkgs, lib, ... }: 2 3with lib; 4 5let 6 cfg = config.services.plex; 7in 8{ 9 imports = [ 10 (mkRemovedOptionModule [ "services" "plex" "managePlugins" ] "Please omit or define the option: `services.plex.extraPlugins' instead.") 11 ]; 12 13 options = { 14 services.plex = { 15 enable = mkEnableOption "Plex Media Server"; 16 17 dataDir = mkOption { 18 type = types.str; 19 default = "/var/lib/plex"; 20 description = '' 21 The directory where Plex stores its data files. 22 ''; 23 }; 24 25 openFirewall = mkOption { 26 type = types.bool; 27 default = false; 28 description = '' 29 Open ports in the firewall for the media server. 30 ''; 31 }; 32 33 user = mkOption { 34 type = types.str; 35 default = "plex"; 36 description = '' 37 User account under which Plex runs. 38 ''; 39 }; 40 41 group = mkOption { 42 type = types.str; 43 default = "plex"; 44 description = '' 45 Group under which Plex runs. 46 ''; 47 }; 48 49 extraPlugins = mkOption { 50 type = types.listOf types.path; 51 default = []; 52 description = '' 53 A list of paths to extra plugin bundles to install in Plex's plugin 54 directory. Every time the systemd unit for Plex starts up, all of the 55 symlinks in Plex's plugin directory will be cleared and this module 56 will symlink all of the paths specified here to that directory. 57 ''; 58 example = literalExpression '' 59 [ 60 (builtins.path { 61 name = "Audnexus.bundle"; 62 path = pkgs.fetchFromGitHub { 63 owner = "djdembeck"; 64 repo = "Audnexus.bundle"; 65 rev = "v0.2.8"; 66 sha256 = "sha256-IWOSz3vYL7zhdHan468xNc6C/eQ2C2BukQlaJNLXh7E="; 67 }; 68 }) 69 ] 70 ''; 71 }; 72 73 extraScanners = mkOption { 74 type = types.listOf types.path; 75 default = []; 76 description = '' 77 A list of paths to extra scanners to install in Plex's scanners 78 directory. 79 80 Every time the systemd unit for Plex starts up, all of the symlinks 81 in Plex's scanners directory will be cleared and this module will 82 symlink all of the paths specified here to that directory. 83 ''; 84 example = literalExpression '' 85 [ 86 (fetchFromGitHub { 87 owner = "ZeroQI"; 88 repo = "Absolute-Series-Scanner"; 89 rev = "773a39f502a1204b0b0255903cee4ed02c46fde0"; 90 sha256 = "4l+vpiDdC8L/EeJowUgYyB3JPNTZ1sauN8liFAcK+PY="; 91 }) 92 ] 93 ''; 94 }; 95 96 accelerationDevices = mkOption { 97 type = types.listOf types.str; 98 default = ["*"]; 99 example = [ "/dev/dri/renderD128" ]; 100 description = '' 101 A list of device paths to hardware acceleration devices that Plex should 102 have access to. This is useful when transcoding media files. 103 The special value `"*"` will allow all devices. 104 ''; 105 }; 106 107 package = mkPackageOption pkgs "plex" { 108 extraDescription = '' 109 Plex subscribers may wish to use their own package here, 110 pointing to subscriber-only server versions. 111 ''; 112 }; 113 }; 114 }; 115 116 config = mkIf cfg.enable { 117 # Most of this is just copied from the RPM package's systemd service file. 118 systemd.services.plex = { 119 description = "Plex Media Server"; 120 after = [ "network.target" ]; 121 wantedBy = [ "multi-user.target" ]; 122 123 serviceConfig = { 124 Type = "simple"; 125 User = cfg.user; 126 Group = cfg.group; 127 128 # Run the pre-start script with full permissions (the "!" prefix) so it 129 # can create the data directory if necessary. 130 ExecStartPre = let 131 preStartScript = pkgs.writeScript "plex-run-prestart" '' 132 #!${pkgs.bash}/bin/bash 133 134 # Create data directory if it doesn't exist 135 if ! test -d "$PLEX_DATADIR"; then 136 echo "Creating initial Plex data directory in: $PLEX_DATADIR" 137 install -d -m 0755 -o "${cfg.user}" -g "${cfg.group}" "$PLEX_DATADIR" 138 fi 139 ''; 140 in 141 "!${preStartScript}"; 142 143 ExecStart = "${cfg.package}/bin/plexmediaserver"; 144 KillSignal = "SIGQUIT"; 145 PIDFile = "${cfg.dataDir}/Plex Media Server/plexmediaserver.pid"; 146 Restart = "on-failure"; 147 148 # Hardening 149 NoNewPrivileges = true; 150 PrivateTmp = true; 151 PrivateDevices = cfg.accelerationDevices == []; 152 DeviceAllow = mkIf (cfg.accelerationDevices != [] && !lib.elem "*" cfg.accelerationDevices) cfg.accelerationDevices; 153 ProtectSystem = true; 154 ProtectHome = true; 155 ProtectControlGroups = true; 156 ProtectKernelModules = true; 157 ProtectKernelTunables = true; 158 RestrictAddressFamilies = ["AF_UNIX" "AF_INET" "AF_INET6" "AF_NETLINK"]; 159 # This could be made to work if the namespaces needed were known 160 # RestrictNamespaces = true; 161 RestrictRealtime = true; 162 RestrictSUIDSGID = true; 163 MemoryDenyWriteExecute = true; 164 LockPersonality = true; 165 }; 166 167 environment = { 168 # Configuration for our FHS userenv script 169 PLEX_DATADIR=cfg.dataDir; 170 PLEX_PLUGINS=concatMapStringsSep ":" builtins.toString cfg.extraPlugins; 171 PLEX_SCANNERS=concatMapStringsSep ":" builtins.toString cfg.extraScanners; 172 173 # The following variables should be set by the FHS userenv script: 174 # PLEX_MEDIA_SERVER_APPLICATION_SUPPORT_DIR 175 # PLEX_MEDIA_SERVER_HOME 176 177 # Allow access to GPU acceleration; the Plex LD_LIBRARY_PATH is added 178 # by the FHS userenv script. 179 LD_LIBRARY_PATH="/run/opengl-driver/lib"; 180 181 PLEX_MEDIA_SERVER_MAX_PLUGIN_PROCS="6"; 182 PLEX_MEDIA_SERVER_TMPDIR="/tmp"; 183 PLEX_MEDIA_SERVER_USE_SYSLOG="true"; 184 LC_ALL="en_US.UTF-8"; 185 LANG="en_US.UTF-8"; 186 }; 187 }; 188 189 networking.firewall = mkIf cfg.openFirewall { 190 allowedTCPPorts = [ 32400 3005 8324 32469 ]; 191 allowedUDPPorts = [ 1900 5353 32410 32412 32413 32414 ]; 192 }; 193 194 users.users = mkIf (cfg.user == "plex") { 195 plex = { 196 group = cfg.group; 197 uid = config.ids.uids.plex; 198 }; 199 }; 200 201 users.groups = mkIf (cfg.group == "plex") { 202 plex = { 203 gid = config.ids.gids.plex; 204 }; 205 }; 206 }; 207}