{ pkgs, ... }: let domain = "stream.mizuki.guru"; hlsDir = "/var/lib/nginx-rtmp/hls"; caddyCertDir = "/var/lib/caddy/.local/share/caddy/certificates/acme-v02.api.letsencrypt.org-directory/${domain}"; in { services.nginx = { enable = true; package = pkgs.nginx.override { modules = [ pkgs.nginxModules.rtmp ]; }; appendConfig = '' rtmp { server { listen 127.0.0.1:19350; chunk_size 4096; application live { live on; record off; hls on; hls_path ${hlsDir}; hls_fragment 2s; hls_playlist_length 10s; hls_cleanup on; } } } ''; }; systemd.tmpfiles.rules = [ "d /var/lib/nginx-rtmp 0755 nginx nginx - -" "d ${hlsDir} 0755 nginx nginx - -" ]; systemd.services.nginx.serviceConfig.ReadWritePaths = [ "/var/lib/nginx-rtmp" ]; systemd.services.stream-stunnel = { description = "RTMPS TLS terminator for stream.mizuki.guru"; after = [ "network-online.target" "caddy.service" "nginx.service" ]; wants = [ "network-online.target" ]; requires = [ "nginx.service" ]; wantedBy = [ "multi-user.target" ]; serviceConfig = { Type = "simple"; User = "caddy"; Group = "caddy"; ExecStart = "${pkgs.stunnel}/bin/stunnel ${pkgs.writeText "stream-stunnel.conf" '' foreground = yes pid = [stream] accept = 0.0.0.0:1935 connect = 127.0.0.1:19350 cert = ${caddyCertDir}/${domain}.crt key = ${caddyCertDir}/${domain}.key ''}"; Restart = "always"; RestartSec = 5; }; }; networking.firewall.allowedTCPPorts = [ 1935 ]; }