1{
2 config,
3 lib,
4 pkgs,
5 utils,
6 ...
7}:
8let
9 cfg = config.services.livekit.ingress;
10 format = pkgs.formats.yaml { };
11 settings = lib.filterAttrsRecursive (_: v: v != null) cfg.settings;
12
13 isLocallyDistributed = config.services.livekit.enable;
14in
15{
16 meta.maintainers = with lib.maintainers; [ k900 ];
17 options.services.livekit.ingress = {
18 enable = lib.mkEnableOption "the livekit ingress service";
19 package = lib.mkPackageOption pkgs "livekit-ingress" { };
20
21 openFirewall = {
22 rtc = lib.mkOption {
23 type = lib.types.bool;
24 default = false;
25 description = "Open WebRTC ports in the firewall.";
26 };
27
28 rtmp = lib.mkOption {
29 type = lib.types.bool;
30 default = false;
31 description = "Open RTMP port in the firewall.";
32 };
33
34 whip = lib.mkOption {
35 type = lib.types.bool;
36 default = false;
37 description = "Open WHIP port in the firewall.";
38 };
39 };
40
41 settings = lib.mkOption {
42 type = lib.types.submodule {
43 freeformType = format.type;
44 options = {
45 rtmp_port = lib.mkOption {
46 type = lib.types.port;
47 default = 1935;
48 description = "TCP port for RTMP connections";
49 };
50
51 whip_port = lib.mkOption {
52 type = lib.types.port;
53 default = 8080;
54 description = "TCP port for WHIP connections";
55 };
56
57 redis = {
58 address = lib.mkOption {
59 type = with lib.types; nullOr str;
60 default =
61 if isLocallyDistributed then
62 "${config.services.livekit.redis.host}:${toString config.services.livekit.redis.port}"
63 else
64 null;
65 example = "redis.example.com:6379";
66 defaultText = "Host and port of the local livekit redis instance, if enabled, or null";
67 description = "Address or hostname and port for redis connection";
68 };
69
70 };
71
72 rtc_config = {
73 port_range_start = lib.mkOption {
74 type = lib.types.port;
75 default = 50000;
76 description = "Start of UDP port range for WebRTC";
77 };
78
79 port_range_end = lib.mkOption {
80 type = lib.types.port;
81 default = 51000;
82 description = "End of UDP port range for WebRTC";
83 };
84
85 use_external_ip = lib.mkOption {
86 type = lib.types.bool;
87 default = false;
88 description = ''
89 When set to true, attempts to discover the host's public IP via STUN.
90 This is useful for cloud environments such as AWS & Google where hosts have an internal IP that maps to an external one.
91 '';
92 };
93 };
94 };
95 };
96 default = { };
97 description = ''
98 LiveKit Ingress configuration.
99
100 See <https://github.com/livekit/ingress?tab=readme-ov-file#config> for possible options.
101 '';
102 example = {
103 prometheus_port = 9039;
104 cpu_cost = {
105 rtmp_cpu_cost = 3.0;
106 whip_cpu_cost = 1.0;
107 };
108 };
109 };
110
111 environmentFile = lib.mkOption {
112 type = lib.types.nullOr lib.types.path;
113 default = null;
114 description = ''
115 Environment file as defined in {manpage}`systemd.exec(5)` passed to the service.
116
117 Use this to specify `LIVEKIT_API_KEY` and `LIVEKIT_API_SECRET`.
118 '';
119 };
120 };
121
122 config = lib.mkIf cfg.enable {
123 networking.firewall = {
124 allowedTCPPorts = lib.mkMerge [
125 (lib.mkIf cfg.openFirewall.rtmp [ cfg.settings.rtmp_port ])
126 (lib.mkIf cfg.openFirewall.whip [ cfg.settings.whip_port ])
127 ];
128 allowedUDPPortRanges = lib.mkIf cfg.openFirewall.rtc [
129 {
130 from = cfg.settings.rtc_config.port_range_start;
131 to = cfg.settings.rtc_config.port_range_end;
132 }
133 ];
134 };
135
136 systemd.services.livekit-ingress = {
137 description = "LiveKit Ingress server";
138 documentation = [ "https://docs.livekit.io/home/self-hosting/ingress/" ];
139 wantedBy = [ "multi-user.target" ];
140 wants = [ "network-online.target" ];
141 after = [ "network-online.target" ];
142
143 serviceConfig = {
144 ExecStart = utils.escapeSystemdExecArgs [
145 (lib.getExe cfg.package)
146 "--config=${format.generate "ingress.yaml" settings}"
147 ];
148 EnvironmentFile = lib.mkIf (cfg.environmentFile != null) cfg.environmentFile;
149 DynamicUser = true;
150 LockPersonality = true;
151 MemoryDenyWriteExecute = true;
152 ProtectClock = true;
153 ProtectControlGroups = true;
154 ProtectHostname = true;
155 ProtectKernelLogs = true;
156 ProtectKernelModules = true;
157 ProtectKernelTunables = true;
158 PrivateDevices = true;
159 PrivateMounts = true;
160 PrivateUsers = true;
161 RestrictAddressFamilies = [
162 "AF_INET"
163 "AF_INET6"
164 "AF_NETLINK"
165 ];
166 RestrictNamespaces = true;
167 RestrictRealtime = true;
168 ProtectHome = true;
169 SystemCallArchitectures = "native";
170 SystemCallFilter = [
171 "@system-service"
172 "~@privileged"
173 "~@resources"
174 ];
175 Restart = "on-failure";
176 RestartSec = 5;
177 UMask = "077";
178 };
179 };
180 };
181}