17 cfg = config.services.frigate;
19 format = pkgs.formats.yaml { };
21 filteredConfig = lib.converge (lib.filterAttrsRecursive (_: v: ! lib.elem v [ null ])) cfg.settings;
23 cameraFormat = with types; submodule {
24 freeformType = format.type;
29 List of inputs for this camera.
31 type = listOf (submodule {
32 freeformType = format.type;
36 example = "rtsp://192.0.2.1:554/rtsp";
42 type = listOf (enum [ "audio" "detect" "record" ]);
43 example = [ "detect" "record" ];
45 List of roles for this stream
57 # Send a subrequest to verify if the user is authenticated and has permission to access the resource.
60 # Save the upstream metadata response headers from Authelia to variables.
61 auth_request_set $user $upstream_http_remote_user;
62 auth_request_set $groups $upstream_http_remote_groups;
63 auth_request_set $name $upstream_http_remote_name;
64 auth_request_set $email $upstream_http_remote_email;
66 # Inject the metadata response headers from the variables into the request made to the backend.
67 proxy_set_header Remote-User $user;
68 proxy_set_header Remote-Groups $groups;
69 proxy_set_header Remote-Email $email;
70 proxy_set_header Remote-Name $name;
72 # Refresh the cookie as needed
73 auth_request_set $auth_cookie $upstream_http_set_cookie;
74 add_header Set-Cookie $auth_cookie;
76 # Pass the location header back up if it exists
77 auth_request_set $redirection_url $upstream_http_location;
78 add_header Location $redirection_url;
81 nginxProxySettings = ''
82 # Basic Proxy Configuration
83 client_body_buffer_size 128k;
84 proxy_next_upstream error timeout invalid_header http_500 http_502 http_503; ## Timeout if the real server is dead.
85 proxy_redirect http:// $scheme://;
86 proxy_cache_bypass $cookie_session;
87 proxy_no_cache $cookie_session;
88 proxy_buffers 64 256k;
90 # Advanced Proxy Configuration
92 proxy_read_timeout 360;
93 proxy_send_timeout 360;
94 proxy_connect_timeout 360;
100 meta.buildDocsInSandbox = false;
102 options.services.frigate = with types; {
103 enable = mkEnableOption "Frigate NVR";
105 package = mkPackageOption pkgs "frigate" { };
107 hostname = mkOption {
109 example = "frigate.exampe.com";
111 Hostname of the nginx vhost to configure.
113 Only nginx is supported by upstream for direct reverse proxying.
117 settings = mkOption {
119 freeformType = format.type;
122 type = attrsOf cameraFormat;
124 Attribute set of cameras configurations.
126 https://docs.frigate.video/configuration/cameras
133 default = "/var/lib/frigate/frigate.db";
135 Path to the SQLite database used
141 enabled = mkEnableOption "MQTT support";
146 example = "mqtt.example.com";
156 Frigate configuration as a nix attribute set.
158 See the project documentation for how to configure frigate.
159 - [Creating a config file](https://docs.frigate.video/guides/getting_started)
160 - [Configuration reference](https://docs.frigate.video/configuration/index)
165 config = mkIf cfg.enable {
168 additionalModules = with pkgs.nginxModules; [
174 recommendedProxySettings = mkDefault true;
175 recommendedGzipSettings = mkDefault true;
176 mapHashBucketSize = mkDefault 128;
178 frigate-api.servers = {
179 "127.0.0.1:5001" = { };
181 frigate-mqtt-ws.servers = {
182 "127.0.0.1:5002" = { };
184 frigate-jsmpeg.servers = {
185 "127.0.0.1:8082" = { };
187 frigate-go2rtc.servers = {
188 "127.0.0.1:1984" = { };
191 proxyCachePath."frigate" = {
193 keysZoneSize = "10m";
194 keysZoneName = "frigate_api_cache";
199 # Based on https://github.com/blakeblackshear/frigate/blob/v0.13.1/docker/main/rootfs/usr/local/nginx/conf/nginx.conf
200 virtualHosts."${cfg.hostname}" = {
204 proxyPass = "http://frigate-api/auth";
208 # Strip all request headers
209 proxy_pass_request_headers off;
211 # Pass info about the request
212 proxy_set_header X-Original-Method $request_method;
213 proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
214 proxy_set_header X-Server-Port $server_port;
215 proxy_set_header Content-Length "";
217 # Pass along auth related info
218 proxy_set_header Authorization $http_authorization;
219 proxy_set_header Cookie $http_cookie;
220 proxy_set_header X-CSRF-TOKEN "1";
222 # Pass headers for common auth proxies
223 proxy_set_header Remote-User $http_remote_user;
224 proxy_set_header Remote-Groups $http_remote_groups;
225 proxy_set_header Remote-Email $http_remote_email;
226 proxy_set_header Remote-Name $http_remote_name;
227 proxy_set_header X-Forwarded-User $http_x_forwarded_user;
228 proxy_set_header X-Forwarded-Groups $http_x_forwarded_groups;
229 proxy_set_header X-Forwarded-Email $http_x_forwarded_email;
230 proxy_set_header X-Forwarded-Preferred-Username $http_x_forwarded_preferred_username;
231 proxy_set_header X-authentik-username $http_x_authentik_username;
232 proxy_set_header X-authentik-groups $http_x_authentik_groups;
233 proxy_set_header X-authentik-email $http_x_authentik_email;
234 proxy_set_header X-authentik-name $http_x_authentik_name;
235 proxy_set_header X-authentik-uid $http_x_authentik_uid;
237 ${nginxProxySettings}
241 extraConfig = nginxAuthRequest + ''
246 secure_token_types application/vnd.apple.mpegurl;
248 add_header Cache-Control "no-store";
253 alias = "/var/cache/frigate/stream/";
254 extraConfig = nginxAuthRequest + ''
255 add_header Cache-Control "no-store";
259 application/dash+xml mpd;
260 application/vnd.apple.mpegurl m3u8;
267 root = "/var/lib/frigate";
268 extraConfig = nginxAuthRequest + ''
275 add_header Cache-Control "public";
280 alias = "/var/cache/frigate/";
286 root = "/var/lib/frigate";
287 extraConfig = nginxAuthRequest + ''
293 autoindex_format json;
297 root = "/var/lib/frigate";
298 extraConfig = nginxAuthRequest + ''
304 autoindex_format json;
308 proxyPass = "http://frigate-mqtt-ws/";
309 proxyWebsockets = true;
310 extraConfig = nginxAuthRequest + nginxProxySettings;
313 proxyPass = "http://frigate-jsmpeg/";
314 proxyWebsockets = true;
315 extraConfig = nginxAuthRequest + nginxProxySettings;
317 # frigate lovelace card uses this path
318 "/live/mse/api/ws" = {
319 proxyPass = "http://frigate-go2rtc/api/ws";
320 proxyWebsockets = true;
321 extraConfig = nginxAuthRequest + nginxProxySettings + ''
327 "/live/webrtc/api/ws" = {
328 proxyPass = "http://frigate-go2rtc/api/ws";
329 proxyWebsockets = true;
330 extraConfig = nginxAuthRequest + nginxProxySettings + ''
336 # pass through go2rtc player
337 "/live/webrtc/webrtc.html" = {
338 proxyPass = "http://frigate-go2rtc/webrtc.html";
339 extraConfig = nginxAuthRequest + nginxProxySettings + ''
345 # frontend uses this to fetch the version
346 "/api/go2rtc/api" = {
347 proxyPass = "http://frigate-go2rtc/api";
348 extraConfig = nginxAuthRequest + nginxProxySettings + ''
354 # integrationn uses this to add webrtc candidate
355 "/api/go2rtc/webrtc" = {
356 proxyPass = "http://frigate-go2rtc/api/webrtc";
357 proxyWebsockets = true;
358 extraConfig = nginxAuthRequest + nginxProxySettings + ''
364 "~* /api/.*\.(jpg|jpeg|png|webp|gif)$" = {
365 proxyPass = "http://frigate-api";
366 extraConfig = nginxAuthRequest + nginxProxySettings + ''
367 rewrite ^/api/(.*)$ $1 break;
371 proxyPass = "http://frigate-api/";
372 extraConfig = nginxAuthRequest + nginxProxySettings + ''
373 add_header Cache-Control "no-store";
376 proxy_cache frigate_api_cache;
378 proxy_cache_use_stale updating;
379 proxy_cache_valid 200 5s;
380 proxy_cache_bypass $http_x_cache_bypass;
381 proxy_no_cache $should_not_cache;
382 add_header X-Cache-Status $upstream_cache_status;
386 proxy_pass http://frigate-api/vod/;
388 add_header Cache-Control "no-store";
389 ${nginxProxySettings}
392 location /api/login {
394 rewrite ^/api(/.*)$ $1 break;
395 proxy_pass http://frigate-api;
396 ${nginxProxySettings}
399 location /api/stats {
402 rewrite ^/api/(.*)$ $1 break;
403 add_header Cache-Control "no-store";
404 proxy_pass http://frigate-api;
405 ${nginxProxySettings}
408 location /api/version {
411 rewrite ^/api/(.*)$ $1 break;
412 add_header Cache-Control "no-store";
413 proxy_pass http://frigate-api;
414 ${nginxProxySettings}
419 root = cfg.package.web;
423 add_header Cache-Control "public";
427 root = cfg.package.web;
428 tryFiles = "$uri $uri.html $uri/ /index.html";
430 add_header Cache-Control "no-store";
438 vod_segments_base_url "";
440 vod_max_mapping_response_size 1m;
441 vod_upstream_location /api;
442 vod_align_segments_to_key_frames on;
443 vod_manifest_segment_durations_mode accurate;
444 vod_ignore_edit_list on;
445 vod_segment_duration 10000;
446 vod_hls_mpegts_align_frames off;
447 vod_hls_mpegts_interleave_frames on;
449 # file handle caching / aio
450 open_file_cache max=1000 inactive=5m;
451 open_file_cache_valid 2m;
452 open_file_cache_min_uses 1;
453 open_file_cache_errors on;
456 # https://github.com/kaltura/nginx-vod-module#vod_open_file_thread_pool
457 vod_open_file_thread_pool default;
460 vod_metadata_cache metadata_cache 512m;
461 vod_mapping_cache mapping_cache 5m 10m;
464 gzip_types application/vnd.apple.mpegurl;
472 allow publish 127.0.0.1;
483 appendHttpConfig = ''
484 map $sent_http_content_type $should_not_cache {
485 'application/json' 0;
491 systemd.services.nginx.serviceConfig.SupplementaryGroups = [
495 users.users.frigate = {
499 users.groups.frigate = { };
501 systemd.services.frigate = {
510 CONFIG_FILE = format.generate "frigate.yml" filteredConfig;
511 HOME = "/var/lib/frigate";
512 PYTHONPATH = cfg.package.pythonPath;
516 # config.boot.kernelPackages.nvidiaPackages.latest.bin
521 ] ++ lib.optionals (!stdenv.hostPlatform.isAarch64) [
522 # not available on aarch64-linux
526 ExecStart = "${cfg.package.python.interpreter} -m frigate";
527 Restart = "on-failure";
534 StateDirectory = "frigate";
535 StateDirectoryMode = "0750";
539 CacheDirectory = "frigate";
540 CacheDirectoryMode = "0750";
543 RuntimeDirectory = "frigate";