1{ config, lib, pkgs, ... }:
2
3let
4 inherit (lib) escapeShellArgs mkEnableOption mkIf mkOption types;
5
6 cfg = config.services.loki;
7
8 prettyJSON = conf:
9 pkgs.runCommand "loki-config.json" { } ''
10 echo '${builtins.toJSON conf}' | ${pkgs.jq}/bin/jq 'del(._module)' > $out
11 '';
12
13in {
14 options.services.loki = {
15 enable = mkEnableOption (lib.mdDoc "loki");
16
17 user = mkOption {
18 type = types.str;
19 default = "loki";
20 description = lib.mdDoc ''
21 User under which the Loki service runs.
22 '';
23 };
24
25 group = mkOption {
26 type = types.str;
27 default = "loki";
28 description = lib.mdDoc ''
29 Group under which the Loki service runs.
30 '';
31 };
32
33 dataDir = mkOption {
34 type = types.path;
35 default = "/var/lib/loki";
36 description = lib.mdDoc ''
37 Specify the directory for Loki.
38 '';
39 };
40
41 configuration = mkOption {
42 type = (pkgs.formats.json {}).type;
43 default = {};
44 description = lib.mdDoc ''
45 Specify the configuration for Loki in Nix.
46 '';
47 };
48
49 configFile = mkOption {
50 type = types.nullOr types.path;
51 default = null;
52 description = lib.mdDoc ''
53 Specify a configuration file that Loki should use.
54 '';
55 };
56
57 extraFlags = mkOption {
58 type = types.listOf types.str;
59 default = [];
60 example = [ "--server.http-listen-port=3101" ];
61 description = lib.mdDoc ''
62 Specify a list of additional command line flags,
63 which get escaped and are then passed to Loki.
64 '';
65 };
66 };
67
68 config = mkIf cfg.enable {
69 assertions = [{
70 assertion = (
71 (cfg.configuration == {} -> cfg.configFile != null) &&
72 (cfg.configFile != null -> cfg.configuration == {})
73 );
74 message = ''
75 Please specify either
76 'services.loki.configuration' or
77 'services.loki.configFile'.
78 '';
79 }];
80
81 environment.systemPackages = [ pkgs.grafana-loki ]; # logcli
82
83 users.groups.${cfg.group} = { };
84 users.users.${cfg.user} = {
85 description = "Loki Service User";
86 group = cfg.group;
87 home = cfg.dataDir;
88 createHome = true;
89 isSystemUser = true;
90 };
91
92 systemd.services.loki = {
93 description = "Loki Service Daemon";
94 wantedBy = [ "multi-user.target" ];
95
96 serviceConfig = let
97 conf = if cfg.configFile == null
98 then prettyJSON cfg.configuration
99 else cfg.configFile;
100 in
101 {
102 ExecStart = "${pkgs.grafana-loki}/bin/loki --config.file=${conf} ${escapeShellArgs cfg.extraFlags}";
103 User = cfg.user;
104 Restart = "always";
105 PrivateTmp = true;
106 ProtectHome = true;
107 ProtectSystem = "full";
108 DevicePolicy = "closed";
109 NoNewPrivileges = true;
110 WorkingDirectory = cfg.dataDir;
111 };
112 };
113 };
114}