at 23.11-pre 32 kB view raw
1{ lib, pkgs, config, options, ... }: 2 3let 4 cfg = config.services.peertube; 5 opt = options.services.peertube; 6 7 settingsFormat = pkgs.formats.json {}; 8 configFile = settingsFormat.generate "production.json" cfg.settings; 9 10 env = { 11 NODE_CONFIG_DIR = "/var/lib/peertube/config"; 12 NODE_ENV = "production"; 13 NODE_EXTRA_CA_CERTS = "/etc/ssl/certs/ca-certificates.crt"; 14 NPM_CONFIG_CACHE = "/var/cache/peertube/.npm"; 15 NPM_CONFIG_PREFIX = cfg.package; 16 HOME = cfg.package; 17 }; 18 19 systemCallsList = [ "@cpu-emulation" "@debug" "@keyring" "@ipc" "@memlock" "@mount" "@obsolete" "@privileged" "@setuid" ]; 20 21 cfgService = { 22 # Proc filesystem 23 ProcSubset = "pid"; 24 ProtectProc = "invisible"; 25 # Access write directories 26 UMask = "0027"; 27 # Capabilities 28 CapabilityBoundingSet = ""; 29 # Security 30 NoNewPrivileges = true; 31 # Sandboxing 32 ProtectSystem = "strict"; 33 ProtectHome = true; 34 PrivateTmp = true; 35 PrivateDevices = true; 36 PrivateUsers = true; 37 ProtectClock = true; 38 ProtectHostname = true; 39 ProtectKernelLogs = true; 40 ProtectKernelModules = true; 41 ProtectKernelTunables = true; 42 ProtectControlGroups = true; 43 RestrictNamespaces = true; 44 LockPersonality = true; 45 RestrictRealtime = true; 46 RestrictSUIDSGID = true; 47 RemoveIPC = true; 48 PrivateMounts = true; 49 # System Call Filtering 50 SystemCallArchitectures = "native"; 51 }; 52 53 envFile = pkgs.writeText "peertube.env" (lib.concatMapStrings (s: s + "\n") ( 54 (lib.concatLists (lib.mapAttrsToList (name: value: 55 if value != null then [ 56 "${name}=\"${toString value}\"" 57 ] else [] 58 ) env)))); 59 60 peertubeEnv = pkgs.writeShellScriptBin "peertube-env" '' 61 set -a 62 source "${envFile}" 63 eval -- "\$@" 64 ''; 65 66 peertubeCli = pkgs.writeShellScriptBin "peertube" '' 67 node ~/dist/server/tools/peertube.js $@ 68 ''; 69 70 nginxCommonHeaders = lib.optionalString cfg.enableWebHttps '' 71 add_header Strict-Transport-Security 'max-age=63072000; includeSubDomains'; 72 '' + lib.optionalString config.services.nginx.virtualHosts.${cfg.localDomain}.http3 '' 73 add_header Alt-Svc 'h3=":443"; ma=86400'; 74 '' + '' 75 add_header Access-Control-Allow-Origin '*'; 76 add_header Access-Control-Allow-Methods 'GET, OPTIONS'; 77 add_header Access-Control-Allow-Headers 'Range,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type'; 78 ''; 79 80in { 81 options.services.peertube = { 82 enable = lib.mkEnableOption (lib.mdDoc "Peertube"); 83 84 user = lib.mkOption { 85 type = lib.types.str; 86 default = "peertube"; 87 description = lib.mdDoc "User account under which Peertube runs."; 88 }; 89 90 group = lib.mkOption { 91 type = lib.types.str; 92 default = "peertube"; 93 description = lib.mdDoc "Group under which Peertube runs."; 94 }; 95 96 localDomain = lib.mkOption { 97 type = lib.types.str; 98 example = "peertube.example.com"; 99 description = lib.mdDoc "The domain serving your PeerTube instance."; 100 }; 101 102 listenHttp = lib.mkOption { 103 type = lib.types.port; 104 default = 9000; 105 description = lib.mdDoc "listen port for HTTP server."; 106 }; 107 108 listenWeb = lib.mkOption { 109 type = lib.types.port; 110 default = 9000; 111 description = lib.mdDoc "listen port for WEB server."; 112 }; 113 114 enableWebHttps = lib.mkOption { 115 type = lib.types.bool; 116 default = false; 117 description = lib.mdDoc "Enable or disable HTTPS protocol."; 118 }; 119 120 dataDirs = lib.mkOption { 121 type = lib.types.listOf lib.types.path; 122 default = [ ]; 123 example = [ "/opt/peertube/storage" "/var/cache/peertube" ]; 124 description = lib.mdDoc "Allow access to custom data locations."; 125 }; 126 127 serviceEnvironmentFile = lib.mkOption { 128 type = lib.types.nullOr lib.types.path; 129 default = null; 130 example = "/run/keys/peertube/password-init-root"; 131 description = lib.mdDoc '' 132 Set environment variables for the service. Mainly useful for setting the initial root password. 133 For example write to file: 134 PT_INITIAL_ROOT_PASSWORD=changeme 135 ''; 136 }; 137 138 settings = lib.mkOption { 139 type = settingsFormat.type; 140 example = lib.literalExpression '' 141 { 142 listen = { 143 hostname = "0.0.0.0"; 144 }; 145 log = { 146 level = "debug"; 147 }; 148 storage = { 149 tmp = "/opt/data/peertube/storage/tmp/"; 150 logs = "/opt/data/peertube/storage/logs/"; 151 cache = "/opt/data/peertube/storage/cache/"; 152 }; 153 } 154 ''; 155 description = lib.mdDoc "Configuration for peertube."; 156 }; 157 158 configureNginx = lib.mkOption { 159 type = lib.types.bool; 160 default = false; 161 description = lib.mdDoc "Configure nginx as a reverse proxy for peertube."; 162 }; 163 164 secrets = { 165 secretsFile = lib.mkOption { 166 type = lib.types.nullOr lib.types.path; 167 default = null; 168 example = "/run/secrets/peertube"; 169 description = lib.mdDoc '' 170 Secrets to run PeerTube. 171 Generate one using `openssl rand -hex 32` 172 ''; 173 }; 174 }; 175 176 database = { 177 createLocally = lib.mkOption { 178 type = lib.types.bool; 179 default = false; 180 description = lib.mdDoc "Configure local PostgreSQL database server for PeerTube."; 181 }; 182 183 host = lib.mkOption { 184 type = lib.types.str; 185 default = if cfg.database.createLocally then "/run/postgresql" else null; 186 defaultText = lib.literalExpression '' 187 if config.${opt.database.createLocally} 188 then "/run/postgresql" 189 else null 190 ''; 191 example = "192.168.15.47"; 192 description = lib.mdDoc "Database host address or unix socket."; 193 }; 194 195 port = lib.mkOption { 196 type = lib.types.port; 197 default = 5432; 198 description = lib.mdDoc "Database host port."; 199 }; 200 201 name = lib.mkOption { 202 type = lib.types.str; 203 default = "peertube"; 204 description = lib.mdDoc "Database name."; 205 }; 206 207 user = lib.mkOption { 208 type = lib.types.str; 209 default = "peertube"; 210 description = lib.mdDoc "Database user."; 211 }; 212 213 passwordFile = lib.mkOption { 214 type = lib.types.nullOr lib.types.path; 215 default = null; 216 example = "/run/keys/peertube/password-postgresql"; 217 description = lib.mdDoc "Password for PostgreSQL database."; 218 }; 219 }; 220 221 redis = { 222 createLocally = lib.mkOption { 223 type = lib.types.bool; 224 default = false; 225 description = lib.mdDoc "Configure local Redis server for PeerTube."; 226 }; 227 228 host = lib.mkOption { 229 type = lib.types.nullOr lib.types.str; 230 default = if cfg.redis.createLocally && !cfg.redis.enableUnixSocket then "127.0.0.1" else null; 231 defaultText = lib.literalExpression '' 232 if config.${opt.redis.createLocally} && !config.${opt.redis.enableUnixSocket} 233 then "127.0.0.1" 234 else null 235 ''; 236 description = lib.mdDoc "Redis host."; 237 }; 238 239 port = lib.mkOption { 240 type = lib.types.nullOr lib.types.port; 241 default = if cfg.redis.createLocally && cfg.redis.enableUnixSocket then null else 31638; 242 defaultText = lib.literalExpression '' 243 if config.${opt.redis.createLocally} && config.${opt.redis.enableUnixSocket} 244 then null 245 else 6379 246 ''; 247 description = lib.mdDoc "Redis port."; 248 }; 249 250 passwordFile = lib.mkOption { 251 type = lib.types.nullOr lib.types.path; 252 default = null; 253 example = "/run/keys/peertube/password-redis-db"; 254 description = lib.mdDoc "Password for redis database."; 255 }; 256 257 enableUnixSocket = lib.mkOption { 258 type = lib.types.bool; 259 default = cfg.redis.createLocally; 260 defaultText = lib.literalExpression "config.${opt.redis.createLocally}"; 261 description = lib.mdDoc "Use Unix socket."; 262 }; 263 }; 264 265 smtp = { 266 createLocally = lib.mkOption { 267 type = lib.types.bool; 268 default = false; 269 description = lib.mdDoc "Configure local Postfix SMTP server for PeerTube."; 270 }; 271 272 passwordFile = lib.mkOption { 273 type = lib.types.nullOr lib.types.path; 274 default = null; 275 example = "/run/keys/peertube/password-smtp"; 276 description = lib.mdDoc "Password for smtp server."; 277 }; 278 }; 279 280 package = lib.mkOption { 281 type = lib.types.package; 282 default = pkgs.peertube; 283 defaultText = lib.literalExpression "pkgs.peertube"; 284 description = lib.mdDoc "Peertube package to use."; 285 }; 286 }; 287 288 config = lib.mkIf cfg.enable { 289 assertions = [ 290 { assertion = cfg.serviceEnvironmentFile == null || !lib.hasPrefix builtins.storeDir cfg.serviceEnvironmentFile; 291 message = '' 292 <option>services.peertube.serviceEnvironmentFile</option> points to 293 a file in the Nix store. You should use a quoted absolute path to 294 prevent this. 295 ''; 296 } 297 { assertion = cfg.secrets.secretsFile != null; 298 message = '' 299 <option>services.peertube.secrets.secretsFile</option> needs to be set. 300 ''; 301 } 302 { assertion = !(cfg.redis.enableUnixSocket && (cfg.redis.host != null || cfg.redis.port != null)); 303 message = '' 304 <option>services.peertube.redis.createLocally</option> and redis network connection (<option>services.peertube.redis.host</option> or <option>services.peertube.redis.port</option>) enabled. Disable either of them. 305 ''; 306 } 307 { assertion = cfg.redis.enableUnixSocket || (cfg.redis.host != null && cfg.redis.port != null); 308 message = '' 309 <option>services.peertube.redis.host</option> and <option>services.peertube.redis.port</option> needs to be set if <option>services.peertube.redis.enableUnixSocket</option> is not enabled. 310 ''; 311 } 312 { assertion = cfg.redis.passwordFile == null || !lib.hasPrefix builtins.storeDir cfg.redis.passwordFile; 313 message = '' 314 <option>services.peertube.redis.passwordFile</option> points to 315 a file in the Nix store. You should use a quoted absolute path to 316 prevent this. 317 ''; 318 } 319 { assertion = cfg.database.passwordFile == null || !lib.hasPrefix builtins.storeDir cfg.database.passwordFile; 320 message = '' 321 <option>services.peertube.database.passwordFile</option> points to 322 a file in the Nix store. You should use a quoted absolute path to 323 prevent this. 324 ''; 325 } 326 { assertion = cfg.smtp.passwordFile == null || !lib.hasPrefix builtins.storeDir cfg.smtp.passwordFile; 327 message = '' 328 <option>services.peertube.smtp.passwordFile</option> points to 329 a file in the Nix store. You should use a quoted absolute path to 330 prevent this. 331 ''; 332 } 333 ]; 334 335 services.peertube.settings = lib.mkMerge [ 336 { 337 listen = { 338 port = cfg.listenHttp; 339 }; 340 webserver = { 341 https = (if cfg.enableWebHttps then true else false); 342 hostname = "${cfg.localDomain}"; 343 port = cfg.listenWeb; 344 }; 345 database = { 346 hostname = "${cfg.database.host}"; 347 port = cfg.database.port; 348 name = "${cfg.database.name}"; 349 username = "${cfg.database.user}"; 350 }; 351 redis = { 352 hostname = "${toString cfg.redis.host}"; 353 port = (if cfg.redis.port == null then "" else cfg.redis.port); 354 }; 355 storage = { 356 tmp = lib.mkDefault "/var/lib/peertube/storage/tmp/"; 357 bin = lib.mkDefault "/var/lib/peertube/storage/bin/"; 358 avatars = lib.mkDefault "/var/lib/peertube/storage/avatars/"; 359 videos = lib.mkDefault "/var/lib/peertube/storage/videos/"; 360 streaming_playlists = lib.mkDefault "/var/lib/peertube/storage/streaming-playlists/"; 361 redundancy = lib.mkDefault "/var/lib/peertube/storage/redundancy/"; 362 logs = lib.mkDefault "/var/lib/peertube/storage/logs/"; 363 previews = lib.mkDefault "/var/lib/peertube/storage/previews/"; 364 thumbnails = lib.mkDefault "/var/lib/peertube/storage/thumbnails/"; 365 torrents = lib.mkDefault "/var/lib/peertube/storage/torrents/"; 366 captions = lib.mkDefault "/var/lib/peertube/storage/captions/"; 367 cache = lib.mkDefault "/var/lib/peertube/storage/cache/"; 368 plugins = lib.mkDefault "/var/lib/peertube/storage/plugins/"; 369 well_known = lib.mkDefault "/var/lib/peertube/storage/well_known/"; 370 client_overrides = lib.mkDefault "/var/lib/peertube/storage/client-overrides/"; 371 }; 372 import = { 373 videos = { 374 http = { 375 youtube_dl_release = { 376 python_path = "${pkgs.python3}/bin/python"; 377 }; 378 }; 379 }; 380 }; 381 } 382 (lib.mkIf cfg.redis.enableUnixSocket { redis = { socket = "/run/redis-peertube/redis.sock"; }; }) 383 ]; 384 385 systemd.tmpfiles.rules = [ 386 "d '/var/lib/peertube/config' 0700 ${cfg.user} ${cfg.group} - -" 387 "z '/var/lib/peertube/config' 0700 ${cfg.user} ${cfg.group} - -" 388 "d '/var/lib/peertube/www' 0750 ${cfg.user} ${cfg.group} - -" 389 "z '/var/lib/peertube/www' 0750 ${cfg.user} ${cfg.group} - -" 390 ]; 391 392 systemd.services.peertube-init-db = lib.mkIf cfg.database.createLocally { 393 description = "Initialization database for PeerTube daemon"; 394 after = [ "network.target" "postgresql.service" ]; 395 requires = [ "postgresql.service" ]; 396 397 script = let 398 psqlSetupCommands = pkgs.writeText "peertube-init.sql" '' 399 SELECT 'CREATE USER "${cfg.database.user}"' WHERE NOT EXISTS (SELECT FROM pg_roles WHERE rolname = '${cfg.database.user}')\gexec 400 SELECT 'CREATE DATABASE "${cfg.database.name}" OWNER "${cfg.database.user}" TEMPLATE template0 ENCODING UTF8' WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = '${cfg.database.name}')\gexec 401 \c '${cfg.database.name}' 402 CREATE EXTENSION IF NOT EXISTS pg_trgm; 403 CREATE EXTENSION IF NOT EXISTS unaccent; 404 ''; 405 in "${config.services.postgresql.package}/bin/psql -f ${psqlSetupCommands}"; 406 407 serviceConfig = { 408 Type = "oneshot"; 409 WorkingDirectory = cfg.package; 410 # User and group 411 User = "postgres"; 412 Group = "postgres"; 413 # Sandboxing 414 RestrictAddressFamilies = [ "AF_UNIX" ]; 415 MemoryDenyWriteExecute = true; 416 # System Call Filtering 417 SystemCallFilter = "~" + lib.concatStringsSep " " (systemCallsList ++ [ "@resources" ]); 418 } // cfgService; 419 }; 420 421 systemd.services.peertube = { 422 description = "PeerTube daemon"; 423 after = [ "network.target" ] 424 ++ lib.optional cfg.redis.createLocally "redis-peertube.service" 425 ++ lib.optionals cfg.database.createLocally [ "postgresql.service" "peertube-init-db.service" ]; 426 requires = lib.optional cfg.redis.createLocally "redis-peertube.service" 427 ++ lib.optionals cfg.database.createLocally [ "postgresql.service" "peertube-init-db.service" ]; 428 wantedBy = [ "multi-user.target" ]; 429 430 environment = env; 431 432 path = with pkgs; [ bashInteractive ffmpeg nodejs_18 openssl yarn python3 ]; 433 434 script = '' 435 #!/bin/sh 436 umask 077 437 cat > /var/lib/peertube/config/local.yaml <<EOF 438 ${lib.optionalString (cfg.secrets.secretsFile != null) '' 439 secrets: 440 peertube: '$(cat ${cfg.secrets.secretsFile})' 441 ''} 442 ${lib.optionalString ((!cfg.database.createLocally) && (cfg.database.passwordFile != null)) '' 443 database: 444 password: '$(cat ${cfg.database.passwordFile})' 445 ''} 446 ${lib.optionalString (cfg.redis.passwordFile != null) '' 447 redis: 448 auth: '$(cat ${cfg.redis.passwordFile})' 449 ''} 450 ${lib.optionalString (cfg.smtp.passwordFile != null) '' 451 smtp: 452 password: '$(cat ${cfg.smtp.passwordFile})' 453 ''} 454 EOF 455 umask 027 456 ln -sf ${configFile} /var/lib/peertube/config/production.json 457 ln -sf ${cfg.package}/config/default.yaml /var/lib/peertube/config/default.yaml 458 ln -sf ${cfg.package}/client/dist -T /var/lib/peertube/www/client 459 ln -sf ${cfg.settings.storage.client_overrides} -T /var/lib/peertube/www/client-overrides 460 npm start 461 ''; 462 serviceConfig = { 463 Type = "simple"; 464 Restart = "always"; 465 RestartSec = 20; 466 TimeoutSec = 60; 467 WorkingDirectory = cfg.package; 468 SyslogIdentifier = "peertube"; 469 # User and group 470 User = cfg.user; 471 Group = cfg.group; 472 # State directory and mode 473 StateDirectory = "peertube"; 474 StateDirectoryMode = "0750"; 475 # Cache directory and mode 476 CacheDirectory = "peertube"; 477 CacheDirectoryMode = "0750"; 478 # Access write directories 479 ReadWritePaths = cfg.dataDirs; 480 # Environment 481 EnvironmentFile = cfg.serviceEnvironmentFile; 482 # Sandboxing 483 RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" "AF_NETLINK" ]; 484 MemoryDenyWriteExecute = false; 485 # System Call Filtering 486 SystemCallFilter = [ ("~" + lib.concatStringsSep " " systemCallsList) "pipe" "pipe2" ]; 487 } // cfgService; 488 }; 489 490 services.nginx = lib.mkIf cfg.configureNginx { 491 enable = true; 492 virtualHosts."${cfg.localDomain}" = { 493 root = "/var/lib/peertube/www"; 494 495 # Application 496 locations."/" = { 497 tryFiles = "/dev/null @api"; 498 priority = 1110; 499 }; 500 501 locations."= /api/v1/videos/upload-resumable" = { 502 tryFiles = "/dev/null @api"; 503 priority = 1120; 504 505 extraConfig = '' 506 client_max_body_size 0; 507 proxy_request_buffering off; 508 ''; 509 }; 510 511 locations."~ ^/api/v1/videos/(upload|([^/]+/studio/edit))$" = { 512 tryFiles = "/dev/null @api"; 513 root = cfg.settings.storage.tmp; 514 priority = 1130; 515 516 extraConfig = '' 517 client_max_body_size 12G; 518 add_header X-File-Maximum-Size 8G always; 519 '' + lib.optionalString cfg.enableWebHttps '' 520 add_header Strict-Transport-Security 'max-age=63072000; includeSubDomains'; 521 '' + lib.optionalString config.services.nginx.virtualHosts.${cfg.localDomain}.http3 '' 522 add_header Alt-Svc 'h3=":443"; ma=86400'; 523 ''; 524 }; 525 526 locations."~ ^/api/v1/(videos|video-playlists|video-channels|users/me)" = { 527 tryFiles = "/dev/null @api"; 528 priority = 1140; 529 530 extraConfig = '' 531 client_max_body_size 6M; 532 add_header X-File-Maximum-Size 4M always; 533 '' + lib.optionalString cfg.enableWebHttps '' 534 add_header Strict-Transport-Security 'max-age=63072000; includeSubDomains'; 535 '' + lib.optionalString config.services.nginx.virtualHosts.${cfg.localDomain}.http3 '' 536 add_header Alt-Svc 'h3=":443"; ma=86400'; 537 ''; 538 }; 539 540 locations."@api" = { 541 proxyPass = "http://127.0.0.1:${toString cfg.listenHttp}"; 542 priority = 1150; 543 544 extraConfig = '' 545 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 546 proxy_set_header Host $host; 547 proxy_set_header X-Real-IP $remote_addr; 548 549 proxy_connect_timeout 10m; 550 551 proxy_send_timeout 10m; 552 proxy_read_timeout 10m; 553 554 client_max_body_size 100k; 555 send_timeout 10m; 556 ''; 557 }; 558 559 # Websocket 560 locations."/socket.io" = { 561 tryFiles = "/dev/null @api_websocket"; 562 priority = 1210; 563 }; 564 565 locations."/tracker/socket" = { 566 tryFiles = "/dev/null @api_websocket"; 567 priority = 1220; 568 569 extraConfig = '' 570 proxy_read_timeout 15m; 571 ''; 572 }; 573 574 locations."~ ^/plugins/[^/]+(/[^/]+)?/ws/" = { 575 tryFiles = "/dev/null @api_websocket"; 576 priority = 1230; 577 }; 578 579 locations."@api_websocket" = { 580 proxyPass = "http://127.0.0.1:${toString cfg.listenHttp}"; 581 priority = 1240; 582 583 extraConfig = '' 584 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 585 proxy_set_header Host $host; 586 proxy_set_header X-Real-IP $remote_addr; 587 proxy_set_header Upgrade $http_upgrade; 588 proxy_set_header Connection 'upgrade'; 589 590 proxy_http_version 1.1; 591 ''; 592 }; 593 594 # Bypass PeerTube for performance reasons. 595 locations."~ ^/client/(assets/images/(icons/icon-36x36\.png|icons/icon-48x48\.png|icons/icon-72x72\.png|icons/icon-96x96\.png|icons/icon-144x144\.png|icons/icon-192x192\.png|icons/icon-512x512\.png|logo\.svg|favicon\.png|default-playlist\.jpg|default-avatar-account\.png|default-avatar-account-48x48\.png|default-avatar-video-channel\.png|default-avatar-video-channel-48x48\.png))$" = { 596 tryFiles = "/client-overrides/$1 /client/$1 $1"; 597 priority = 1310; 598 }; 599 600 locations."~ ^/client/(.*\.(js|css|png|svg|woff2|otf|ttf|woff|eot))$" = { 601 alias = "${cfg.package}/client/dist/$1"; 602 priority = 1320; 603 extraConfig = '' 604 add_header Cache-Control 'public, max-age=604800, immutable'; 605 '' + lib.optionalString cfg.enableWebHttps '' 606 add_header Strict-Transport-Security 'max-age=63072000; includeSubDomains'; 607 '' + lib.optionalString config.services.nginx.virtualHosts.${cfg.localDomain}.http3 '' 608 add_header Alt-Svc 'h3=":443"; ma=86400'; 609 ''; 610 }; 611 612 locations."^~ /lazy-static/avatars/" = { 613 tryFiles = "$uri @api"; 614 root = cfg.settings.storage.avatars; 615 priority = 1330; 616 extraConfig = '' 617 if ($request_method = 'OPTIONS') { 618 ${nginxCommonHeaders} 619 add_header Access-Control-Max-Age 1728000; 620 add_header Cache-Control 'no-cache'; 621 add_header Content-Type 'text/plain charset=UTF-8'; 622 add_header Content-Length 0; 623 return 204; 624 } 625 626 ${nginxCommonHeaders} 627 add_header Cache-Control 'public, max-age=7200'; 628 629 rewrite ^/lazy-static/avatars/(.*)$ /$1 break; 630 ''; 631 }; 632 633 locations."^~ /lazy-static/banners/" = { 634 tryFiles = "$uri @api"; 635 root = cfg.settings.storage.avatars; 636 priority = 1340; 637 extraConfig = '' 638 if ($request_method = 'OPTIONS') { 639 ${nginxCommonHeaders} 640 add_header Access-Control-Max-Age 1728000; 641 add_header Cache-Control 'no-cache'; 642 add_header Content-Type 'text/plain charset=UTF-8'; 643 add_header Content-Length 0; 644 return 204; 645 } 646 647 ${nginxCommonHeaders} 648 add_header Cache-Control 'public, max-age=7200'; 649 650 rewrite ^/lazy-static/banners/(.*)$ /$1 break; 651 ''; 652 }; 653 654 locations."^~ /lazy-static/previews/" = { 655 tryFiles = "$uri @api"; 656 root = cfg.settings.storage.previews; 657 priority = 1350; 658 extraConfig = '' 659 if ($request_method = 'OPTIONS') { 660 ${nginxCommonHeaders} 661 add_header Access-Control-Max-Age 1728000; 662 add_header Cache-Control 'no-cache'; 663 add_header Content-Type 'text/plain charset=UTF-8'; 664 add_header Content-Length 0; 665 return 204; 666 } 667 668 ${nginxCommonHeaders} 669 add_header Cache-Control 'public, max-age=7200'; 670 671 rewrite ^/lazy-static/previews/(.*)$ /$1 break; 672 ''; 673 }; 674 675 locations."^~ /static/streaming-playlists/private/" = { 676 proxyPass = "http://127.0.0.1:${toString cfg.listenHttp}"; 677 priority = 1410; 678 extraConfig = '' 679 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 680 proxy_set_header Host $host; 681 proxy_set_header X-Real-IP $remote_addr; 682 683 proxy_limit_rate 5M; 684 ''; 685 }; 686 687 locations."^~ /static/webseed/private/" = { 688 proxyPass = "http://127.0.0.1:${toString cfg.listenHttp}"; 689 priority = 1420; 690 extraConfig = '' 691 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 692 proxy_set_header Host $host; 693 proxy_set_header X-Real-IP $remote_addr; 694 695 proxy_limit_rate 5M; 696 ''; 697 }; 698 699 locations."^~ /static/thumbnails/" = { 700 tryFiles = "$uri @api"; 701 root = cfg.settings.storage.thumbnails; 702 priority = 1430; 703 extraConfig = '' 704 if ($request_method = 'OPTIONS') { 705 ${nginxCommonHeaders} 706 add_header Access-Control-Max-Age 1728000; 707 add_header Cache-Control 'no-cache'; 708 add_header Content-Type 'text/plain charset=UTF-8'; 709 add_header Content-Length 0; 710 return 204; 711 } 712 713 ${nginxCommonHeaders} 714 add_header Cache-Control 'public, max-age=7200'; 715 716 rewrite ^/static/thumbnails/(.*)$ /$1 break; 717 ''; 718 }; 719 720 locations."^~ /static/redundancy/" = { 721 tryFiles = "$uri @api"; 722 root = cfg.settings.storage.redundancy; 723 priority = 1440; 724 extraConfig = '' 725 set $peertube_limit_rate 800k; 726 727 if ($request_uri ~ -fragmented.mp4$) { 728 set $peertube_limit_rate 5M; 729 } 730 731 if ($request_method = 'OPTIONS') { 732 ${nginxCommonHeaders} 733 add_header Access-Control-Max-Age 1728000; 734 add_header Content-Type 'text/plain charset=UTF-8'; 735 add_header Content-Length 0; 736 return 204; 737 } 738 if ($request_method = 'GET') { 739 ${nginxCommonHeaders} 740 741 access_log off; 742 } 743 744 aio threads; 745 sendfile on; 746 sendfile_max_chunk 1M; 747 748 limit_rate $peertube_limit_rate; 749 limit_rate_after 5M; 750 751 rewrite ^/static/redundancy/(.*)$ /$1 break; 752 ''; 753 }; 754 755 locations."^~ /static/streaming-playlists/" = { 756 tryFiles = "$uri @api"; 757 root = cfg.settings.storage.streaming_playlists; 758 priority = 1450; 759 extraConfig = '' 760 set $peertube_limit_rate 800k; 761 762 if ($request_uri ~ -fragmented.mp4$) { 763 set $peertube_limit_rate 5M; 764 } 765 766 if ($request_method = 'OPTIONS') { 767 ${nginxCommonHeaders} 768 add_header Access-Control-Max-Age 1728000; 769 add_header Content-Type 'text/plain charset=UTF-8'; 770 add_header Content-Length 0; 771 return 204; 772 } 773 if ($request_method = 'GET') { 774 ${nginxCommonHeaders} 775 776 access_log off; 777 } 778 779 aio threads; 780 sendfile on; 781 sendfile_max_chunk 1M; 782 783 limit_rate $peertube_limit_rate; 784 limit_rate_after 5M; 785 786 rewrite ^/static/streaming-playlists/(.*)$ /$1 break; 787 ''; 788 }; 789 790 locations."^~ /static/webseed/" = { 791 tryFiles = "$uri @api"; 792 root = cfg.settings.storage.videos; 793 priority = 1460; 794 extraConfig = '' 795 set $peertube_limit_rate 800k; 796 797 if ($request_uri ~ -fragmented.mp4$) { 798 set $peertube_limit_rate 5M; 799 } 800 801 if ($request_method = 'OPTIONS') { 802 ${nginxCommonHeaders} 803 add_header Access-Control-Max-Age 1728000; 804 add_header Content-Type 'text/plain charset=UTF-8'; 805 add_header Content-Length 0; 806 return 204; 807 } 808 if ($request_method = 'GET') { 809 ${nginxCommonHeaders} 810 811 access_log off; 812 } 813 814 aio threads; 815 sendfile on; 816 sendfile_max_chunk 1M; 817 818 limit_rate $peertube_limit_rate; 819 limit_rate_after 5M; 820 821 rewrite ^/static/webseed/(.*)$ /$1 break; 822 ''; 823 }; 824 825 extraConfig = lib.optionalString cfg.enableWebHttps '' 826 add_header Strict-Transport-Security 'max-age=63072000; includeSubDomains'; 827 ''; 828 }; 829 }; 830 831 services.postgresql = lib.mkIf cfg.database.createLocally { 832 enable = true; 833 }; 834 835 services.redis.servers.peertube = lib.mkMerge [ 836 (lib.mkIf cfg.redis.createLocally { 837 enable = true; 838 }) 839 (lib.mkIf (cfg.redis.createLocally && !cfg.redis.enableUnixSocket) { 840 bind = "127.0.0.1"; 841 port = cfg.redis.port; 842 }) 843 (lib.mkIf (cfg.redis.createLocally && cfg.redis.enableUnixSocket) { 844 unixSocket = "/run/redis-peertube/redis.sock"; 845 unixSocketPerm = 660; 846 }) 847 ]; 848 849 services.postfix = lib.mkIf cfg.smtp.createLocally { 850 enable = true; 851 hostname = lib.mkDefault "${cfg.localDomain}"; 852 }; 853 854 users.users = lib.mkMerge [ 855 (lib.mkIf (cfg.user == "peertube") { 856 peertube = { 857 isSystemUser = true; 858 group = cfg.group; 859 home = cfg.package; 860 }; 861 }) 862 (lib.attrsets.setAttrByPath [ cfg.user "packages" ] [ cfg.package peertubeEnv peertubeCli pkgs.ffmpeg pkgs.nodejs_18 pkgs.yarn ]) 863 (lib.mkIf cfg.redis.enableUnixSocket {${config.services.peertube.user}.extraGroups = [ "redis-peertube" ];}) 864 ]; 865 866 users.groups = { 867 ${cfg.group} = { 868 members = lib.optional cfg.configureNginx config.services.nginx.user; 869 }; 870 }; 871 }; 872}