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