1 { config, pkgs, lib, ... }:
6 cfg = config.services.netdata;
8 wrappedPlugins = pkgs.runCommand "wrapped-plugins" { preferLocalBuild = true; } ''
9 mkdir -p $out/libexec/netdata/plugins.d
10 ln -s /run/wrappers/bin/apps.plugin $out/libexec/netdata/plugins.d/apps.plugin
11 ln -s /run/wrappers/bin/cgroup-network $out/libexec/netdata/plugins.d/cgroup-network
12 ln -s /run/wrappers/bin/perf.plugin $out/libexec/netdata/plugins.d/perf.plugin
13 ln -s /run/wrappers/bin/slabinfo.plugin $out/libexec/netdata/plugins.d/slabinfo.plugin
14 ln -s /run/wrappers/bin/freeipmi.plugin $out/libexec/netdata/plugins.d/freeipmi.plugin
18 "${cfg.package}/libexec/netdata/plugins.d"
19 "${wrappedPlugins}/libexec/netdata/plugins.d"
20 ] ++ cfg.extraPluginPaths;
22 configDirectory = pkgs.runCommand "netdata-config-d" { } ''
24 ${concatStringsSep "\n" (mapAttrsToList (path: file: ''
25 mkdir -p "$out/$(dirname ${path})"
26 ln -s "${file}" "$out/${path}"
32 "config directory" = "/etc/netdata/conf.d";
33 "plugins directory" = concatStringsSep " " plugins;
36 "web files owner" = "root";
37 "web files group" = "root";
40 "script to get cgroup network interfaces" = "${wrappedPlugins}/libexec/netdata/plugins.d/cgroup-network";
41 "use unified cgroups" = "yes";
44 mkConfig = generators.toINI {} (recursiveUpdate localConfig cfg.config);
45 configFile = pkgs.writeText "netdata.conf" (if cfg.configText != null then cfg.configText else mkConfig);
47 defaultUser = "netdata";
52 enable = mkEnableOption (lib.mdDoc "netdata");
56 default = pkgs.netdata;
57 defaultText = literalExpression "pkgs.netdata";
58 description = lib.mdDoc "Netdata package to use.";
64 description = lib.mdDoc "User account under which netdata runs.";
70 description = lib.mdDoc "Group under which netdata runs.";
73 configText = mkOption {
74 type = types.nullOr types.lines;
75 description = lib.mdDoc "Verbatim netdata.conf, cannot be combined with config.";
89 description = lib.mdDoc ''
90 Whether to enable python-based plugins
93 extraPackages = mkOption {
94 type = types.functionTo (types.listOf types.package);
96 defaultText = literalExpression "ps: []";
97 example = literalExpression ''
104 description = lib.mdDoc ''
105 Extra python packages available at runtime
106 to enable additional python plugins.
111 extraPluginPaths = mkOption {
112 type = types.listOf types.path;
114 example = literalExpression ''
115 [ "/path/to/plugins.d" ]
117 description = lib.mdDoc ''
118 Extra paths to add to the netdata global "plugins directory"
119 option. Useful for when you want to include your own
122 Details about writing a custom netdata plugin are available at:
123 <https://docs.netdata.cloud/collectors/plugins.d/>
125 Cannot be combined with configText.
130 type = types.attrsOf types.attrs;
132 description = lib.mdDoc "netdata.conf configuration as nix attributes. cannot be combined with configText.";
133 example = literalExpression ''
135 "debug log" = "syslog";
136 "access log" = "syslog";
137 "error log" = "syslog";
142 configDir = mkOption {
143 type = types.attrsOf types.path;
145 description = lib.mdDoc ''
146 Complete netdata config directory except netdata.conf.
147 The default configuration is merged with changes
148 defined in this option.
149 Each top-level attribute denotes a path in the configuration
150 directory as in environment.etc.
151 Its value is the absolute path and must be readable by netdata.
152 Cannot be combined with configText.
154 example = literalExpression ''
155 "health_alarm_notify.conf" = pkgs.writeText "health_alarm_notify.conf" '''
156 sendmail="/path/to/sendmail"
158 "health.d" = "/run/secrets/netdata/health.d";
162 enableAnalyticsReporting = mkOption {
165 description = lib.mdDoc ''
166 Enable reporting of anonymous usage statistics to Netdata Inc. via either
167 Google Analytics (in versions prior to 1.29.4), or Netdata Inc.'s
168 self-hosted PostHog (in versions 1.29.4 and later).
169 See: <https://learn.netdata.cloud/docs/agent/anonymous-statistics>
175 config = mkIf cfg.enable {
177 [ { assertion = cfg.config != {} -> cfg.configText == null ;
178 message = "Cannot specify both config and configText";
182 environment.etc."netdata/netdata.conf".source = configFile;
183 environment.etc."netdata/conf.d".source = configDirectory;
185 systemd.services.netdata = {
186 description = "Real time performance monitoring";
187 after = [ "network.target" ];
188 wantedBy = [ "multi-user.target" ];
189 path = (with pkgs; [ curl gawk iproute2 which procps bash ])
190 ++ lib.optional cfg.python.enable (pkgs.python3.withPackages cfg.python.extraPackages)
191 ++ lib.optional config.virtualisation.libvirtd.enable (config.virtualisation.libvirtd.package);
193 PYTHONPATH = "${cfg.package}/libexec/netdata/python.d/python_modules";
194 } // lib.optionalAttrs (!cfg.enableAnalyticsReporting) {
198 config.environment.etc."netdata/netdata.conf".source
199 config.environment.etc."netdata/conf.d".source
202 ExecStart = "${cfg.package}/bin/netdata -P /run/netdata/netdata.pid -D -c /etc/netdata/netdata.conf";
203 ExecReload = "${pkgs.util-linux}/bin/kill -s HUP -s USR1 -s USR2 $MAINPID";
204 ExecStartPost = pkgs.writeShellScript "wait-for-netdata-up" ''
205 while [ "$(${pkgs.netdata}/bin/netdatacli ping)" != pong ]; do sleep 0.5; done
209 Restart = "on-failure";
214 LimitNOFILE = "30000";
215 # Runtime directory and mode
216 RuntimeDirectory = "netdata";
217 RuntimeDirectoryMode = "0750";
218 # State directory and mode
219 StateDirectory = "netdata";
220 StateDirectoryMode = "0750";
221 # Cache directory and mode
222 CacheDirectory = "netdata";
223 CacheDirectoryMode = "0750";
224 # Logs directory and mode
225 LogsDirectory = "netdata";
226 LogsDirectoryMode = "0750";
227 # Configuration directory and mode
228 ConfigurationDirectory = "netdata";
229 ConfigurationDirectoryMode = "0755";
231 CapabilityBoundingSet = [
232 "CAP_DAC_OVERRIDE" # is required for freeipmi and slabinfo plugins
233 "CAP_DAC_READ_SEARCH" # is required for apps plugin
234 "CAP_FOWNER" # is required for freeipmi plugin
235 "CAP_SETPCAP" # is required for apps, perf and slabinfo plugins
236 "CAP_SYS_ADMIN" # is required for perf plugin
237 "CAP_SYS_PTRACE" # is required for apps plugin
238 "CAP_SYS_RESOURCE" # is required for ebpf plugin
239 "CAP_NET_RAW" # is required for fping app
240 "CAP_SYS_CHROOT" # is required for cgroups plugin
241 "CAP_SETUID" # is required for cgroups and cgroups-network plugins
244 ProtectSystem = "full";
245 ProtectHome = "read-only";
247 ProtectControlGroups = true;
248 PrivateMounts = true;
252 systemd.enableCgroupAccounting = true;
254 security.wrappers = {
256 source = "${cfg.package}/libexec/netdata/plugins.d/apps.plugin.org";
257 capabilities = "cap_dac_read_search,cap_sys_ptrace+ep";
260 permissions = "u+rx,g+x,o-rwx";
264 source = "${cfg.package}/libexec/netdata/plugins.d/cgroup-network.org";
265 capabilities = "cap_setuid+ep";
268 permissions = "u+rx,g+x,o-rwx";
272 source = "${cfg.package}/libexec/netdata/plugins.d/perf.plugin.org";
273 capabilities = "cap_sys_admin+ep";
276 permissions = "u+rx,g+x,o-rwx";
279 "slabinfo.plugin" = {
280 source = "${cfg.package}/libexec/netdata/plugins.d/slabinfo.plugin.org";
281 capabilities = "cap_dac_override+ep";
284 permissions = "u+rx,g+x,o-rwx";
287 } // optionalAttrs (cfg.package.withIpmi) {
288 "freeipmi.plugin" = {
289 source = "${cfg.package}/libexec/netdata/plugins.d/freeipmi.plugin.org";
290 capabilities = "cap_dac_override,cap_fowner+ep";
293 permissions = "u+rx,g+x,o-rwx";
297 security.pam.loginLimits = [
298 { domain = "netdata"; type = "soft"; item = "nofile"; value = "10000"; }
299 { domain = "netdata"; type = "hard"; item = "nofile"; value = "30000"; }
302 users.users = optionalAttrs (cfg.user == defaultUser) {
309 users.groups = optionalAttrs (cfg.group == defaultUser) {
310 ${defaultUser} = { };