1 { options, config, lib, pkgs, ... }:
6 cfg = config.services.grafana;
7 opt = options.services.grafana;
8 provisioningSettingsFormat = pkgs.formats.yaml {};
9 declarativePlugins = pkgs.linkFarm "grafana-plugins" (builtins.map (pkg: { name = pkg.pname; path = pkg; }) cfg.declarativePlugins);
10 useMysql = cfg.settings.database.type == "mysql";
11 usePostgresql = cfg.settings.database.type == "postgres";
13 settingsFormatIni = pkgs.formats.ini {};
14 configFile = settingsFormatIni.generate "config.ini" cfg.settings;
16 datasourceConfiguration = {
18 datasources = cfg.provision.datasources;
21 datasourceFileNew = if (cfg.provision.datasources.path == null) then provisioningSettingsFormat.generate "datasource.yaml" cfg.provision.datasources.settings else cfg.provision.datasources.path;
22 datasourceFile = if (builtins.isList cfg.provision.datasources) then provisioningSettingsFormat.generate "datasource.yaml" datasourceConfiguration else datasourceFileNew;
24 dashboardConfiguration = {
26 providers = cfg.provision.dashboards;
29 dashboardFileNew = if (cfg.provision.dashboards.path == null) then provisioningSettingsFormat.generate "dashboard.yaml" cfg.provision.dashboards.settings else cfg.provision.dashboards.path;
30 dashboardFile = if (builtins.isList cfg.provision.dashboards) then provisioningSettingsFormat.generate "dashboard.yaml" dashboardConfiguration else dashboardFileNew;
32 notifierConfiguration = {
34 notifiers = cfg.provision.notifiers;
37 notifierFile = pkgs.writeText "notifier.yaml" (builtins.toJSON notifierConfiguration);
39 generateAlertingProvisioningYaml = x: if (cfg.provision.alerting."${x}".path == null)
40 then provisioningSettingsFormat.generate "${x}.yaml" cfg.provision.alerting."${x}".settings
41 else cfg.provision.alerting."${x}".path;
42 rulesFile = generateAlertingProvisioningYaml "rules";
43 contactPointsFile = generateAlertingProvisioningYaml "contactPoints";
44 policiesFile = generateAlertingProvisioningYaml "policies";
45 templatesFile = generateAlertingProvisioningYaml "templates";
46 muteTimingsFile = generateAlertingProvisioningYaml "muteTimings";
48 provisionConfDir = pkgs.runCommand "grafana-provisioning" { } ''
49 mkdir -p $out/{datasources,dashboards,notifiers,alerting}
50 ln -sf ${datasourceFile} $out/datasources/datasource.yaml
51 ln -sf ${dashboardFile} $out/dashboards/dashboard.yaml
52 ln -sf ${notifierFile} $out/notifiers/notifier.yaml
53 ln -sf ${rulesFile} $out/alerting/rules.yaml
54 ln -sf ${contactPointsFile} $out/alerting/contactPoints.yaml
55 ln -sf ${policiesFile} $out/alerting/policies.yaml
56 ln -sf ${templatesFile} $out/alerting/templates.yaml
57 ln -sf ${muteTimingsFile} $out/alerting/muteTimings.yaml
60 # Get a submodule without any embedded metadata:
61 _filter = x: filterAttrs (k: v: k != "_module") x;
63 # http://docs.grafana.org/administration/provisioning/#datasources
64 grafanaTypes.datasourceConfig = types.submodule {
65 freeformType = provisioningSettingsFormat.type;
70 description = lib.mdDoc "Name of the datasource. Required.";
74 description = lib.mdDoc "Datasource type. Required.";
77 type = types.enum ["proxy" "direct"];
79 description = lib.mdDoc "Access mode. proxy or direct (Server or Browser in the UI). Required.";
82 type = types.nullOr types.str;
84 description = lib.mdDoc "Custom UID which can be used to reference this datasource in other parts of the configuration, if not specified will be generated automatically.";
88 default = "localhost";
89 description = lib.mdDoc "Url of the datasource.";
94 description = lib.mdDoc "Allow users to edit datasources from the UI.";
97 type = types.nullOr types.str;
99 description = lib.mdDoc ''
100 Database password, if used. Please note that the contents of this option
101 will end up in a world-readable Nix store. Use the file provider
102 pointing at a reasonably secured file in the local filesystem
103 to work around that. Look at the documentation for details:
104 <https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/#file-provider>
107 basicAuthPassword = mkOption {
108 type = types.nullOr types.str;
110 description = lib.mdDoc ''
111 Basic auth password. Please note that the contents of this option
112 will end up in a world-readable Nix store. Use the file provider
113 pointing at a reasonably secured file in the local filesystem
114 to work around that. Look at the documentation for details:
115 <https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/#file-provider>
118 secureJsonData = mkOption {
119 type = types.nullOr types.attrs;
121 description = lib.mdDoc ''
122 Datasource specific secure configuration. Please note that the contents of this option
123 will end up in a world-readable Nix store. Use the file provider
124 pointing at a reasonably secured file in the local filesystem
125 to work around that. Look at the documentation for details:
126 <https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/#file-provider>
132 # http://docs.grafana.org/administration/provisioning/#dashboards
133 grafanaTypes.dashboardConfig = types.submodule {
134 freeformType = provisioningSettingsFormat.type;
140 description = lib.mdDoc "A unique provider name.";
145 description = lib.mdDoc "Dashboard provider type.";
147 options.path = mkOption {
149 description = lib.mdDoc "Path grafana will watch for dashboards. Required when using the 'file' type.";
154 grafanaTypes.notifierConfig = types.submodule {
159 description = lib.mdDoc "Notifier name.";
162 type = types.enum ["dingding" "discord" "email" "googlechat" "hipchat" "kafka" "line" "teams" "opsgenie" "pagerduty" "prometheus-alertmanager" "pushover" "sensu" "sensugo" "slack" "telegram" "threema" "victorops" "webhook"];
163 description = lib.mdDoc "Notifier type.";
167 description = lib.mdDoc "Unique notifier identifier.";
172 description = lib.mdDoc "Organization ID.";
174 org_name = mkOption {
176 default = "Main Org.";
177 description = lib.mdDoc "Organization name.";
179 is_default = mkOption {
181 description = lib.mdDoc "Is the default notifier.";
184 send_reminder = mkOption {
187 description = lib.mdDoc "Should the notifier be sent reminder notifications while alerts continue to fire.";
189 frequency = mkOption {
192 description = lib.mdDoc "How frequently should the notifier be sent reminders.";
194 disable_resolve_message = mkOption {
197 description = lib.mdDoc "Turn off the message that sends when an alert returns to OK.";
199 settings = mkOption {
200 type = types.nullOr types.attrs;
202 description = lib.mdDoc "Settings for the notifier type.";
204 secure_settings = mkOption {
205 type = types.nullOr types.attrs;
207 description = lib.mdDoc ''
208 Secure settings for the notifier type. Please note that the contents of this option
209 will end up in a world-readable Nix store. Use the file provider
210 pointing at a reasonably secured file in the local filesystem
211 to work around that. Look at the documentation for details:
212 <https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/#file-provider>
219 (mkRenamedOptionModule [ "services" "grafana" "protocol" ] [ "services" "grafana" "settings" "server" "protocol" ])
220 (mkRenamedOptionModule [ "services" "grafana" "addr" ] [ "services" "grafana" "settings" "server" "http_addr" ])
221 (mkRenamedOptionModule [ "services" "grafana" "port" ] [ "services" "grafana" "settings" "server" "http_port" ])
222 (mkRenamedOptionModule [ "services" "grafana" "domain" ] [ "services" "grafana" "settings" "server" "domain" ])
223 (mkRenamedOptionModule [ "services" "grafana" "rootUrl" ] [ "services" "grafana" "settings" "server" "root_url" ])
224 (mkRenamedOptionModule [ "services" "grafana" "staticRootPath" ] [ "services" "grafana" "settings" "server" "static_root_path" ])
225 (mkRenamedOptionModule [ "services" "grafana" "certFile" ] [ "services" "grafana" "settings" "server" "cert_file" ])
226 (mkRenamedOptionModule [ "services" "grafana" "certKey" ] [ "services" "grafana" "settings" "server" "cert_key" ])
227 (mkRenamedOptionModule [ "services" "grafana" "socket" ] [ "services" "grafana" "settings" "server" "socket" ])
228 (mkRenamedOptionModule [ "services" "grafana" "database" "type" ] [ "services" "grafana" "settings" "database" "type" ])
229 (mkRenamedOptionModule [ "services" "grafana" "database" "host" ] [ "services" "grafana" "settings" "database" "host" ])
230 (mkRenamedOptionModule [ "services" "grafana" "database" "name" ] [ "services" "grafana" "settings" "database" "name" ])
231 (mkRenamedOptionModule [ "services" "grafana" "database" "user" ] [ "services" "grafana" "settings" "database" "user" ])
232 (mkRenamedOptionModule [ "services" "grafana" "database" "password" ] [ "services" "grafana" "settings" "database" "password" ])
233 (mkRenamedOptionModule [ "services" "grafana" "database" "path" ] [ "services" "grafana" "settings" "database" "path" ])
234 (mkRenamedOptionModule [ "services" "grafana" "database" "connMaxLifetime" ] [ "services" "grafana" "settings" "database" "conn_max_lifetime" ])
235 (mkRenamedOptionModule [ "services" "grafana" "security" "adminUser" ] [ "services" "grafana" "settings" "security" "admin_user" ])
236 (mkRenamedOptionModule [ "services" "grafana" "security" "adminPassword" ] [ "services" "grafana" "settings" "security" "admin_password" ])
237 (mkRenamedOptionModule [ "services" "grafana" "security" "secretKey" ] [ "services" "grafana" "settings" "security" "secret_key" ])
238 (mkRenamedOptionModule [ "services" "grafana" "server" "serveFromSubPath" ] [ "services" "grafana" "settings" "server" "serve_from_sub_path" ])
239 (mkRenamedOptionModule [ "services" "grafana" "smtp" "enable" ] [ "services" "grafana" "settings" "smtp" "enabled" ])
240 (mkRenamedOptionModule [ "services" "grafana" "smtp" "user" ] [ "services" "grafana" "settings" "smtp" "user" ])
241 (mkRenamedOptionModule [ "services" "grafana" "smtp" "password" ] [ "services" "grafana" "settings" "smtp" "password" ])
242 (mkRenamedOptionModule [ "services" "grafana" "smtp" "fromAddress" ] [ "services" "grafana" "settings" "smtp" "from_address" ])
243 (mkRenamedOptionModule [ "services" "grafana" "users" "allowSignUp" ] [ "services" "grafana" "settings" "users" "allow_sign_up" ])
244 (mkRenamedOptionModule [ "services" "grafana" "users" "allowOrgCreate" ] [ "services" "grafana" "settings" "users" "allow_org_create" ])
245 (mkRenamedOptionModule [ "services" "grafana" "users" "autoAssignOrg" ] [ "services" "grafana" "settings" "users" "auto_assign_org" ])
246 (mkRenamedOptionModule [ "services" "grafana" "users" "autoAssignOrgRole" ] [ "services" "grafana" "settings" "users" "auto_assign_org_role" ])
247 (mkRenamedOptionModule [ "services" "grafana" "auth" "disableLoginForm" ] [ "services" "grafana" "settings" "auth" "disable_login_form" ])
248 (mkRenamedOptionModule [ "services" "grafana" "auth" "anonymous" "enable" ] [ "services" "grafana" "settings" "auth.anonymous" "enabled" ])
249 (mkRenamedOptionModule [ "services" "grafana" "auth" "anonymous" "org_name" ] [ "services" "grafana" "settings" "auth.anonymous" "org_name" ])
250 (mkRenamedOptionModule [ "services" "grafana" "auth" "anonymous" "org_role" ] [ "services" "grafana" "settings" "auth.anonymous" "org_role" ])
251 (mkRenamedOptionModule [ "services" "grafana" "auth" "azuread" "enable" ] [ "services" "grafana" "settings" "auth.azuread" "enabled" ])
252 (mkRenamedOptionModule [ "services" "grafana" "auth" "azuread" "allowSignUp" ] [ "services" "grafana" "settings" "auth.azuread" "allow_sign_up" ])
253 (mkRenamedOptionModule [ "services" "grafana" "auth" "azuread" "clientId" ] [ "services" "grafana" "settings" "auth.azuread" "client_id" ])
254 (mkRenamedOptionModule [ "services" "grafana" "auth" "azuread" "allowedDomains" ] [ "services" "grafana" "settings" "auth.azuread" "allowed_domains" ])
255 (mkRenamedOptionModule [ "services" "grafana" "auth" "azuread" "allowedGroups" ] [ "services" "grafana" "settings" "auth.azuread" "allowed_groups" ])
256 (mkRenamedOptionModule [ "services" "grafana" "auth" "google" "enable" ] [ "services" "grafana" "settings" "auth.google" "enabled" ])
257 (mkRenamedOptionModule [ "services" "grafana" "auth" "google" "allowSignUp" ] [ "services" "grafana" "settings" "auth.google" "allow_sign_up" ])
258 (mkRenamedOptionModule [ "services" "grafana" "auth" "google" "clientId" ] [ "services" "grafana" "settings" "auth.google" "client_id" ])
259 (mkRenamedOptionModule [ "services" "grafana" "analytics" "reporting" "enable" ] [ "services" "grafana" "settings" "analytics" "reporting_enabled" ])
261 (mkRemovedOptionModule [ "services" "grafana" "database" "passwordFile" ] ''
262 This option has been removed. Use 'services.grafana.settings.database.password' with file provider instead.
264 (mkRemovedOptionModule [ "services" "grafana" "security" "adminPasswordFile" ] ''
265 This option has been removed. Use 'services.grafana.settings.security.admin_password' with file provider instead.
267 (mkRemovedOptionModule [ "services" "grafana" "security" "secretKeyFile" ] ''
268 This option has been removed. Use 'services.grafana.settings.security.secret_key' with file provider instead.
270 (mkRemovedOptionModule [ "services" "grafana" "smtp" "passwordFile" ] ''
271 This option has been removed. Use 'services.grafana.settings.smtp.password' with file provider instead.
273 (mkRemovedOptionModule [ "services" "grafana" "auth" "azuread" "clientSecretFile" ] ''
274 This option has been removed. Use 'services.grafana.settings.azuread.client_secret' with file provider instead.
276 (mkRemovedOptionModule [ "services" "grafana" "auth" "google" "clientSecretFile" ] ''
277 This option has been removed. Use 'services.grafana.settings.google.client_secret' with file provider instead.
280 (mkRemovedOptionModule [ "services" "grafana" "auth" "azuread" "tenantId" ] "This option has been deprecated upstream.")
283 options.services.grafana = {
284 enable = mkEnableOption (lib.mdDoc "grafana");
286 declarativePlugins = mkOption {
287 type = with types; nullOr (listOf path);
289 description = lib.mdDoc "If non-null, then a list of packages containing Grafana plugins to install. If set, plugins cannot be manually installed.";
290 example = literalExpression "with pkgs.grafanaPlugins; [ grafana-piechart-panel ]";
291 # Make sure each plugin is added only once; otherwise building
292 # the link farm fails, since the same path is added multiple
294 apply = x: if isList x then lib.unique x else x;
298 description = lib.mdDoc "Package to use.";
299 default = pkgs.grafana;
300 defaultText = literalExpression "pkgs.grafana";
301 type = types.package;
305 description = lib.mdDoc "Data directory.";
306 default = "/var/lib/grafana";
310 settings = mkOption {
311 description = lib.mdDoc ''
312 Grafana settings. See <https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/>
313 for available options. INI format is used.
315 type = types.submodule {
316 freeformType = settingsFormatIni.type;
321 description = lib.mdDoc "Directory where grafana will automatically scan and look for plugins";
322 default = if (cfg.declarativePlugins == null) then "${cfg.dataDir}/plugins" else declarativePlugins;
323 defaultText = literalExpression "if (cfg.declarativePlugins == null) then \"\${cfg.dataDir}/plugins\" else declarativePlugins";
327 provisioning = mkOption {
328 description = lib.mdDoc ''
329 Folder that contains provisioning config files that grafana will apply on startup and while running.
330 Don't change the value of this option if you are planning to use `services.grafana.provision` options.
332 default = provisionConfDir;
333 defaultText = literalExpression ''
334 pkgs.runCommand "grafana-provisioning" { } \'\'
335 mkdir -p $out/{datasources,dashboards,notifiers,alerting}
336 ln -sf ''${datasourceFile} $out/datasources/datasource.yaml
337 ln -sf ''${dashboardFile} $out/dashboards/dashboard.yaml
338 ln -sf ''${notifierFile} $out/notifiers/notifier.yaml
339 ln -sf ''${rulesFile} $out/alerting/rules.yaml
340 ln -sf ''${contactPointsFile} $out/alerting/contactPoints.yaml
341 ln -sf ''${policiesFile} $out/alerting/policies.yaml
342 ln -sf ''${templatesFile} $out/alerting/templates.yaml
343 ln -sf ''${muteTimingsFile} $out/alerting/muteTimings.yaml
351 protocol = mkOption {
352 description = lib.mdDoc "Which protocol to listen.";
354 type = types.enum ["http" "https" "h2" "socket"];
357 http_addr = mkOption {
358 description = lib.mdDoc "Listening address.";
363 http_port = mkOption {
364 description = lib.mdDoc "Listening port.";
370 description = lib.mdDoc "The public facing domain name used to access grafana from a browser.";
371 default = "localhost";
375 root_url = mkOption {
376 description = lib.mdDoc "Full public facing url.";
377 default = "%(protocol)s://%(domain)s:%(http_port)s/";
381 static_root_path = mkOption {
382 description = lib.mdDoc "Root path for static assets.";
383 default = "${cfg.package}/share/grafana/public";
384 defaultText = literalExpression ''"''${package}/share/grafana/public"'';
388 enable_gzip = mkOption {
389 description = lib.mdDoc ''
390 Set this option to true to enable HTTP compression, this can improve transfer speed and bandwidth utilization.
391 It is recommended that most users set it to true. By default it is set to false for compatibility reasons.
397 cert_file = mkOption {
398 description = lib.mdDoc "Cert file for ssl.";
403 cert_key = mkOption {
404 description = lib.mdDoc "Cert key for ssl.";
410 description = lib.mdDoc "Path where the socket should be created when protocol=socket. Make sure that Grafana has appropriate permissions before you change this setting.";
411 default = "/run/grafana/grafana.sock";
418 description = lib.mdDoc "Database type.";
420 type = types.enum ["mysql" "sqlite3" "postgres"];
424 description = lib.mdDoc "Database host.";
425 default = "127.0.0.1:3306";
430 description = lib.mdDoc "Database name.";
436 description = lib.mdDoc "Database user.";
441 password = mkOption {
442 description = lib.mdDoc ''
443 Database password. Please note that the contents of this option
444 will end up in a world-readable Nix store. Use the file provider
445 pointing at a reasonably secured file in the local filesystem
446 to work around that. Look at the documentation for details:
447 <https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/#file-provider>
454 description = lib.mdDoc "Only applicable to sqlite3 database. The file path where the database will be stored.";
455 default = "${cfg.dataDir}/data/grafana.db";
456 defaultText = literalExpression ''"''${config.${opt.dataDir}}/data/grafana.db"'';
462 admin_user = mkOption {
463 description = lib.mdDoc "Default admin username.";
468 admin_password = mkOption {
469 description = lib.mdDoc ''
470 Default admin password. Please note that the contents of this option
471 will end up in a world-readable Nix store. Use the file provider
472 pointing at a reasonably secured file in the local filesystem
473 to work around that. Look at the documentation for details:
474 <https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/#file-provider>
480 secret_key = mkOption {
481 description = lib.mdDoc ''
482 Secret key used for signing. Please note that the contents of this option
483 will end up in a world-readable Nix store. Use the file provider
484 pointing at a reasonably secured file in the local filesystem
485 to work around that. Look at the documentation for details:
486 <https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/#file-provider>
488 default = "SW2YcwTIb9zpOOhoPsMm";
495 description = lib.mdDoc "Whether to enable SMTP.";
500 description = lib.mdDoc "Host to connect to.";
501 default = "localhost:25";
505 description = lib.mdDoc "User used for authentication.";
509 password = mkOption {
510 description = lib.mdDoc ''
511 Password used for authentication. Please note that the contents of this option
512 will end up in a world-readable Nix store. Use the file provider
513 pointing at a reasonably secured file in the local filesystem
514 to work around that. Look at the documentation for details:
515 <https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/#file-provider>
520 from_address = mkOption {
521 description = lib.mdDoc "Email address used for sending.";
522 default = "admin@grafana.localhost";
528 allow_sign_up = mkOption {
529 description = lib.mdDoc "Disable user signup / registration.";
534 allow_org_create = mkOption {
535 description = lib.mdDoc "Whether user is allowed to create organizations.";
540 auto_assign_org = mkOption {
541 description = lib.mdDoc "Whether to automatically assign new users to default org.";
546 auto_assign_org_role = mkOption {
547 description = lib.mdDoc "Default role new users will be auto assigned.";
549 type = types.enum ["Viewer" "Editor"];
553 analytics.reporting_enabled = mkOption {
554 description = lib.mdDoc "Whether to allow anonymous usage reporting to stats.grafana.net.";
563 enable = mkEnableOption (lib.mdDoc "provision");
565 datasources = mkOption {
566 description = lib.mdDoc ''
567 Deprecated option for Grafana datasource configuration. Use either
568 `services.grafana.provision.datasources.settings` or
569 `services.grafana.provision.datasources.path` instead.
572 apply = x: if (builtins.isList x) then map _filter x else x;
573 type = with types; either (listOf grafanaTypes.datasourceConfig) (submodule {
574 options.settings = mkOption {
575 description = lib.mdDoc ''
576 Grafana datasource configuration in Nix. Can't be used with
577 `services.grafana.provision.datasources.path` simultaneously. See
578 <https://grafana.com/docs/grafana/latest/administration/provisioning/#data-sources>
579 for supported options.
582 type = types.nullOr (types.submodule {
584 apiVersion = mkOption {
585 description = lib.mdDoc "Config file version.";
590 datasources = mkOption {
591 description = lib.mdDoc "List of datasources to insert/update.";
593 type = types.listOf grafanaTypes.datasourceConfig;
596 deleteDatasources = mkOption {
597 description = lib.mdDoc "List of datasources that should be deleted from the database.";
599 type = types.listOf (types.submodule {
600 options.name = mkOption {
601 description = lib.mdDoc "Name of the datasource to delete.";
605 options.orgId = mkOption {
606 description = lib.mdDoc "Organization ID of the datasource to delete.";
613 example = literalExpression ''
622 deleteDatasources = [{
630 options.path = mkOption {
631 description = lib.mdDoc ''
632 Path to YAML datasource configuration. Can't be used with
633 `services.grafana.provision.datasources.settings` simultaneously.
636 type = types.nullOr types.path;
642 dashboards = mkOption {
643 description = lib.mdDoc ''
644 Deprecated option for Grafana dashboard configuration. Use either
645 `services.grafana.provision.dashboards.settings` or
646 `services.grafana.provision.dashboards.path` instead.
649 apply = x: if (builtins.isList x) then map _filter x else x;
650 type = with types; either (listOf grafanaTypes.dashboardConfig) (submodule {
651 options.settings = mkOption {
652 description = lib.mdDoc ''
653 Grafana dashboard configuration in Nix. Can't be used with
654 `services.grafana.provision.dashboards.path` simultaneously. See
655 <https://grafana.com/docs/grafana/latest/administration/provisioning/#dashboards>
656 for supported options.
659 type = types.nullOr (types.submodule {
660 options.apiVersion = mkOption {
661 description = lib.mdDoc "Config file version.";
666 options.providers = mkOption {
667 description = lib.mdDoc "List of dashboards to insert/update.";
669 type = types.listOf grafanaTypes.dashboardConfig;
672 example = literalExpression ''
678 options.path = "/var/lib/grafana/dashboards";
684 options.path = mkOption {
685 description = lib.mdDoc ''
686 Path to YAML dashboard configuration. Can't be used with
687 `services.grafana.provision.dashboards.settings` simultaneously.
690 type = types.nullOr types.path;
696 notifiers = mkOption {
697 description = lib.mdDoc "Grafana notifier configuration.";
699 type = types.listOf grafanaTypes.notifierConfig;
700 apply = x: map _filter x;
707 description = lib.mdDoc ''
708 Path to YAML rules configuration. Can't be used with
709 `services.grafana.provision.alerting.rules.settings` simultaneously.
712 type = types.nullOr types.path;
715 settings = mkOption {
716 description = lib.mdDoc ''
717 Grafana rules configuration in Nix. Can't be used with
718 `services.grafana.provision.alerting.rules.path` simultaneously. See
719 <https://grafana.com/docs/grafana/latest/administration/provisioning/#rules>
720 for supported options.
723 type = types.nullOr (types.submodule {
725 apiVersion = mkOption {
726 description = lib.mdDoc "Config file version.";
732 description = lib.mdDoc "List of rule groups to import or update.";
734 type = types.listOf (types.submodule {
735 freeformType = provisioningSettingsFormat.type;
737 options.name = mkOption {
738 description = lib.mdDoc "Name of the rule group. Required.";
742 options.folder = mkOption {
743 description = lib.mdDoc "Name of the folder the rule group will be stored in. Required.";
747 options.interval = mkOption {
748 description = lib.mdDoc "Interval that the rule group should be evaluated at. Required.";
754 deleteRules = mkOption {
755 description = lib.mdDoc "List of alert rule UIDs that should be deleted.";
757 type = types.listOf (types.submodule {
758 options.orgId = mkOption {
759 description = lib.mdDoc "Organization ID, default = 1";
764 options.uid = mkOption {
765 description = lib.mdDoc "Unique identifier for the rule. Required.";
772 example = literalExpression ''
778 name = "my_rule_group";
779 folder = "my_first_folder";
783 title = "my_first_rule";
787 datasourceUid = "-100";
794 operator.type = "and";
795 query.params = [ "A" ];
796 reducer.type = "last";
805 maxDataPoints = 43200;
810 dashboardUid = "my_dashboard";
812 noDataState = "Alerting";
814 annotations.some_key = "some_value";
815 labels.team = "sre_team1";
830 description = lib.mdDoc ''
831 Path to YAML contact points configuration. Can't be used with
832 `services.grafana.provision.alerting.contactPoints.settings` simultaneously.
835 type = types.nullOr types.path;
838 settings = mkOption {
839 description = lib.mdDoc ''
840 Grafana contact points configuration in Nix. Can't be used with
841 `services.grafana.provision.alerting.contactPoints.path` simultaneously. See
842 <https://grafana.com/docs/grafana/latest/administration/provisioning/#contact-points>
843 for supported options.
846 type = types.nullOr (types.submodule {
848 apiVersion = mkOption {
849 description = lib.mdDoc "Config file version.";
854 contactPoints = mkOption {
855 description = lib.mdDoc "List of contact points to import or update. Please note that sensitive data will end up in world-readable Nix store.";
857 type = types.listOf (types.submodule {
858 freeformType = provisioningSettingsFormat.type;
860 options.name = mkOption {
861 description = lib.mdDoc "Name of the contact point. Required.";
867 deleteContactPoints = mkOption {
868 description = lib.mdDoc "List of receivers that should be deleted.";
870 type = types.listOf (types.submodule {
871 options.orgId = mkOption {
872 description = lib.mdDoc "Organization ID, default = 1.";
877 options.uid = mkOption {
878 description = lib.mdDoc "Unique identifier for the receiver. Required.";
885 example = literalExpression ''
894 type = "prometheus-alertmanager";
895 settings.url = "http://test:9000";
899 deleteContactPoints = [{
910 description = lib.mdDoc ''
911 Path to YAML notification policies configuration. Can't be used with
912 `services.grafana.provision.alerting.policies.settings` simultaneously.
915 type = types.nullOr types.path;
918 settings = mkOption {
919 description = lib.mdDoc ''
920 Grafana notification policies configuration in Nix. Can't be used with
921 `services.grafana.provision.alerting.policies.path` simultaneously. See
922 <https://grafana.com/docs/grafana/latest/administration/provisioning/#notification-policies>
923 for supported options.
926 type = types.nullOr (types.submodule {
928 apiVersion = mkOption {
929 description = lib.mdDoc "Config file version.";
934 policies = mkOption {
935 description = lib.mdDoc "List of contact points to import or update.";
937 type = types.listOf (types.submodule {
938 freeformType = provisioningSettingsFormat.type;
942 resetPolicies = mkOption {
943 description = lib.mdDoc "List of orgIds that should be reset to the default policy.";
945 type = types.listOf types.int;
949 example = literalExpression ''
955 receiver = "grafana-default-email";
956 group_by = [ "..." ];
958 "alertname = Watchdog"
959 "severity =~ \"warning|critical\""
961 mute_time_intervals = [
965 group_interval = "5m";
966 repeat_interval = "4h";
979 description = lib.mdDoc ''
980 Path to YAML templates configuration. Can't be used with
981 `services.grafana.provision.alerting.templates.settings` simultaneously.
984 type = types.nullOr types.path;
987 settings = mkOption {
988 description = lib.mdDoc ''
989 Grafana templates configuration in Nix. Can't be used with
990 `services.grafana.provision.alerting.templates.path` simultaneously. See
991 <https://grafana.com/docs/grafana/latest/administration/provisioning/#templates>
992 for supported options.
995 type = types.nullOr (types.submodule {
997 apiVersion = mkOption {
998 description = lib.mdDoc "Config file version.";
1003 templates = mkOption {
1004 description = lib.mdDoc "List of templates to import or update.";
1006 type = types.listOf (types.submodule {
1007 freeformType = provisioningSettingsFormat.type;
1009 options.name = mkOption {
1010 description = lib.mdDoc "Name of the template, must be unique. Required.";
1014 options.template = mkOption {
1015 description = lib.mdDoc "Alerting with a custom text template";
1021 deleteTemplates = mkOption {
1022 description = lib.mdDoc "List of alert rule UIDs that should be deleted.";
1024 type = types.listOf (types.submodule {
1025 options.orgId = mkOption {
1026 description = lib.mdDoc "Organization ID, default = 1.";
1031 options.name = mkOption {
1032 description = lib.mdDoc "Name of the template, must be unique. Required.";
1039 example = literalExpression ''
1045 name = "my_first_template";
1046 template = "Alerting with a custom text template";
1049 deleteTemplates = [{
1051 name = "my_first_template";
1060 description = lib.mdDoc ''
1061 Path to YAML mute timings configuration. Can't be used with
1062 `services.grafana.provision.alerting.muteTimings.settings` simultaneously.
1065 type = types.nullOr types.path;
1068 settings = mkOption {
1069 description = lib.mdDoc ''
1070 Grafana mute timings configuration in Nix. Can't be used with
1071 `services.grafana.provision.alerting.muteTimings.path` simultaneously. See
1072 <https://grafana.com/docs/grafana/latest/administration/provisioning/#mute-timings>
1073 for supported options.
1076 type = types.nullOr (types.submodule {
1078 apiVersion = mkOption {
1079 description = lib.mdDoc "Config file version.";
1084 muteTimes = mkOption {
1085 description = lib.mdDoc "List of mute time intervals to import or update.";
1087 type = types.listOf (types.submodule {
1088 freeformType = provisioningSettingsFormat.type;
1090 options.name = mkOption {
1091 description = lib.mdDoc "Name of the mute time interval, must be unique. Required.";
1097 deleteMuteTimes = mkOption {
1098 description = lib.mdDoc "List of mute time intervals that should be deleted.";
1100 type = types.listOf (types.submodule {
1101 options.orgId = mkOption {
1102 description = lib.mdDoc "Organization ID, default = 1.";
1107 options.name = mkOption {
1108 description = lib.mdDoc "Name of the mute time interval, must be unique. Required.";
1115 example = literalExpression ''
1124 start_time = "06:00";
1148 deleteMuteTimes = [{
1160 config = mkIf cfg.enable {
1162 usesFileProvider = opt: defaultValue: builtins.match "^${defaultValue}$|^\\$__file\\{.*}$" opt != null;
1165 ! usesFileProvider cfg.settings.database.password "" ||
1166 ! usesFileProvider cfg.settings.security.admin_password "admin"
1167 ) "Grafana passwords will be stored as plaintext in the Nix store! Use file provider instead.")
1170 checkOpts = opt: any (x: x.password != null || x.basicAuthPassword != null || x.secureJsonData != null) opt;
1171 datasourcesUsed = if (cfg.provision.datasources.settings == null) then [] else cfg.provision.datasources.settings.datasources;
1172 in if (builtins.isList cfg.provision.datasources) then checkOpts cfg.provision.datasources else checkOpts datasourcesUsed
1174 Datasource passwords will be stored as plaintext in the Nix store!
1175 It is not possible to use file provider in provisioning; please provision
1176 datasources via `services.grafana.provision.datasources.path` instead.
1179 any (x: x.secure_settings != null) cfg.provision.notifiers
1180 ) "Notifier secure settings will be stored as plaintext in the Nix store! Use file provider instead.")
1182 builtins.isList cfg.provision.datasources && cfg.provision.datasources != []
1184 Provisioning Grafana datasources with options has been deprecated.
1185 Use `services.grafana.provision.datasources.settings` or
1186 `services.grafana.provision.datasources.path` instead.
1189 builtins.isList cfg.provision.datasources && cfg.provision.dashboards != []
1191 Provisioning Grafana dashboards with options has been deprecated.
1192 Use `services.grafana.provision.dashboards.settings` or
1193 `services.grafana.provision.dashboards.path` instead.
1196 cfg.provision.notifiers != []
1198 Notifiers are deprecated upstream and will be removed in Grafana 10.
1199 Use `services.grafana.provision.alerting.contactPoints` instead.
1203 environment.systemPackages = [ cfg.package ];
1207 assertion = if (builtins.isList cfg.provision.datasources) then true else cfg.provision.datasources.settings == null || cfg.provision.datasources.path == null;
1208 message = "Cannot set both datasources settings and datasources path";
1212 prometheusIsNotDirect = opt: all
1213 ({ type, access, ... }: type == "prometheus" -> access != "direct")
1216 if (builtins.isList cfg.provision.datasources) then prometheusIsNotDirect cfg.provision.datasources
1217 else cfg.provision.datasources.settings == null || prometheusIsNotDirect cfg.provision.datasources.settings.datasources;
1218 message = "For datasources of type `prometheus`, the `direct` access mode is not supported anymore (since Grafana 9.2.0)";
1221 assertion = if (builtins.isList cfg.provision.dashboards) then true else cfg.provision.dashboards.settings == null || cfg.provision.dashboards.path == null;
1222 message = "Cannot set both dashboards settings and dashboards path";
1225 assertion = cfg.provision.alerting.rules.settings == null || cfg.provision.alerting.rules.path == null;
1226 message = "Cannot set both rules settings and rules path";
1229 assertion = cfg.provision.alerting.contactPoints.settings == null || cfg.provision.alerting.contactPoints.path == null;
1230 message = "Cannot set both contact points settings and contact points path";
1233 assertion = cfg.provision.alerting.policies.settings == null || cfg.provision.alerting.policies.path == null;
1234 message = "Cannot set both policies settings and policies path";
1237 assertion = cfg.provision.alerting.templates.settings == null || cfg.provision.alerting.templates.path == null;
1238 message = "Cannot set both templates settings and templates path";
1241 assertion = cfg.provision.alerting.muteTimings.settings == null || cfg.provision.alerting.muteTimings.path == null;
1242 message = "Cannot set both mute timings settings and mute timings path";
1246 systemd.services.grafana = {
1247 description = "Grafana Service Daemon";
1248 wantedBy = ["multi-user.target"];
1249 after = ["networking.target"] ++ lib.optional usePostgresql "postgresql.service" ++ lib.optional useMysql "mysql.service";
1251 set -o errexit -o pipefail -o nounset -o errtrace
1252 shopt -s inherit_errexit
1254 exec ${cfg.package}/bin/grafana-server -homepath ${cfg.dataDir} -config ${configFile}
1257 WorkingDirectory = cfg.dataDir;
1259 RuntimeDirectory = "grafana";
1260 RuntimeDirectoryMode = "0755";
1262 AmbientCapabilities = lib.mkIf (cfg.settings.server.http_port < 1024) [ "CAP_NET_BIND_SERVICE" ];
1263 CapabilityBoundingSet = if (cfg.settings.server.http_port < 1024) then [ "CAP_NET_BIND_SERVICE" ] else [ "" ];
1264 DeviceAllow = [ "" ];
1265 LockPersonality = true;
1266 NoNewPrivileges = true;
1267 PrivateDevices = true;
1269 ProtectClock = true;
1270 ProtectControlGroups = true;
1272 ProtectHostname = true;
1273 ProtectKernelLogs = true;
1274 ProtectKernelModules = true;
1275 ProtectKernelTunables = true;
1276 ProtectProc = "invisible";
1277 ProtectSystem = "full";
1279 RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_UNIX" ];
1280 RestrictNamespaces = true;
1281 RestrictRealtime = true;
1282 RestrictSUIDSGID = true;
1283 SystemCallArchitectures = "native";
1284 # Upstream grafana is not setting SystemCallFilter for compatibility
1285 # reasons, see https://github.com/grafana/grafana/pull/40176
1286 SystemCallFilter = [ "@system-service" "~@privileged" ];
1290 ln -fs ${cfg.package}/share/grafana/conf ${cfg.dataDir}
1291 ln -fs ${cfg.package}/share/grafana/tools ${cfg.dataDir}
1295 users.users.grafana = {
1296 uid = config.ids.uids.grafana;
1297 description = "Grafana user";
1302 users.groups.grafana = {};