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