grafana-alloy: don't build the frontend twice
[NixPkgs.git] / nixos / modules / services / misc / plex.nix
blob212abda5d1e0bd6f061fe6a4939e5e63d9d3871d
1 { config, pkgs, lib, ... }:
3 with lib;
5 let
6   cfg = config.services.plex;
7 in
9   imports = [
10     (mkRemovedOptionModule [ "services" "plex" "managePlugins" ] "Please omit or define the option: `services.plex.extraPlugins' instead.")
11   ];
13   options = {
14     services.plex = {
15       enable = mkEnableOption "Plex Media Server";
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       };
25       openFirewall = mkOption {
26         type = types.bool;
27         default = false;
28         description = ''
29           Open ports in the firewall for the media server.
30         '';
31       };
33       user = mkOption {
34         type = types.str;
35         default = "plex";
36         description = ''
37           User account under which Plex runs.
38         '';
39       };
41       group = mkOption {
42         type = types.str;
43         default = "plex";
44         description = ''
45           Group under which Plex runs.
46         '';
47       };
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       };
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.
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       };
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       };
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   };
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" ];
123       serviceConfig = {
124         Type = "simple";
125         User = cfg.user;
126         Group = cfg.group;
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
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}";
143         ExecStart = "${cfg.package}/bin/plexmediaserver";
144         KillSignal = "SIGQUIT";
145         PIDFile = "${cfg.dataDir}/Plex Media Server/plexmediaserver.pid";
146         Restart = "on-failure";
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       };
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;
173         # The following variables should be set by the FHS userenv script:
174         #   PLEX_MEDIA_SERVER_APPLICATION_SUPPORT_DIR
175         #   PLEX_MEDIA_SERVER_HOME
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";
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     };
189     networking.firewall = mkIf cfg.openFirewall {
190       allowedTCPPorts = [ 32400 3005 8324 32469 ];
191       allowedUDPPorts = [ 1900 5353 32410 32412 32413 32414 ];
192     };
194     users.users = mkIf (cfg.user == "plex") {
195       plex = {
196         group = cfg.group;
197         uid = config.ids.uids.plex;
198       };
199     };
201     users.groups = mkIf (cfg.group == "plex") {
202       plex = {
203         gid = config.ids.gids.plex;
204       };
205     };
206   };