grafana-alloy: don't build the frontend twice
[NixPkgs.git] / nixos / modules / services / web-servers / keter / default.nix
blob8685953d6e9d54af426ff7db5650fd97e61b00ba
1 { config, pkgs, lib, ... }:
2 let
3   cfg = config.services.keter;
4   yaml = pkgs.formats.yaml { };
5 in
7   meta = {
8     maintainers = with lib.maintainers; [ jappie ];
9   };
11   imports = [
12     (lib.mkRenamedOptionModule [ "services" "keter" "keterRoot" ] [ "services" "keter" "root" ])
13     (lib.mkRenamedOptionModule [ "services" "keter" "keterPackage" ] [ "services" "keter" "package" ])
14   ];
16   options.services.keter = {
17     enable = lib.mkEnableOption ''keter, a web app deployment manager.
18 Note that this module only support loading of webapps:
19 Keep an old app running and swap the ports when the new one is booted
20 '';
22     root = lib.mkOption {
23       type = lib.types.str;
24       default = "/var/lib/keter";
25       description = "Mutable state folder for keter";
26     };
28     package = lib.mkOption {
29       type = lib.types.package;
30       default = pkgs.haskellPackages.keter;
31       defaultText = lib.literalExpression "pkgs.haskellPackages.keter";
32       description = "The keter package to be used";
33     };
36     globalKeterConfig = lib.mkOption {
37       type = lib.types.submodule {
38         freeformType = yaml.type;
39         options = {
40           ip-from-header = lib.mkOption {
41             default = true;
42             type = lib.types.bool;
43             description = "You want that ip-from-header in the nginx setup case. It allows nginx setting the original ip address rather then it being localhost (due to reverse proxying)";
44           };
45           listeners = lib.mkOption {
46             default = [{ host = "*"; port = 6981; }];
47             type = lib.types.listOf (lib.types.submodule {
48               options = {
49                 host = lib.mkOption {
50                   type = lib.types.str;
51                   description = "host";
52                 };
53                 port = lib.mkOption {
54                   type = lib.types.port;
55                   description = "port";
56                 };
57               };
58             });
59             description = ''
60               You want that ip-from-header in
61               the nginx setup case.
62               It allows nginx setting the original ip address rather
63               then it being localhost (due to reverse proxying).
64               However if you configure keter to accept connections
65               directly you may want to set this to false.'';
66           };
67           rotate-logs = lib.mkOption {
68             default = false;
69             type = lib.types.bool;
70             description = ''
71               emits keter logs and it's applications to stderr.
72               which allows journald to capture them.
73               Set to true to let keter put the logs in files
74               (useful on non systemd systems, this is the old approach
75               where keter handled log management)'';
76           };
77         };
78       };
79       description = "Global config for keter, see <https://github.com/snoyberg/keter/blob/master/etc/keter-config.yaml> for reference";
80     };
82     bundle = {
83       appName = lib.mkOption {
84         type = lib.types.str;
85         default = "myapp";
86         description = "The name keter assigns to this bundle";
87       };
89       executable = lib.mkOption {
90         type = lib.types.path;
91         description = "The executable to be run";
92       };
94       domain = lib.mkOption {
95         type = lib.types.str;
96         default = "example.com";
97         description = "The domain keter will bind to";
98       };
100       publicScript = lib.mkOption {
101         type = lib.types.str;
102         default = "";
103         description = ''
104           Allows loading of public environment variables,
105           these are emitted to the log so it shouldn't contain secrets.
106         '';
107         example = "ADMIN_EMAIL=hi@example.com";
108       };
110       secretScript = lib.mkOption {
111         type = lib.types.str;
112         default = "";
113         description = "Allows loading of private environment variables";
114         example = "MY_AWS_KEY=$(cat /run/keys/AWS_ACCESS_KEY_ID)";
115       };
116     };
118   };
120   config = lib.mkIf cfg.enable (
121     let
122       incoming = "${cfg.root}/incoming";
125       globalKeterConfigFile = pkgs.writeTextFile {
126         name = "keter-config.yml";
127         text = (lib.generators.toYAML { } (cfg.globalKeterConfig // { root = cfg.root; }));
128       };
130       # If things are expected to change often, put it in the bundle!
131       bundle = pkgs.callPackage ./bundle.nix
132         (cfg.bundle // { keterExecutable = executable; keterDomain = cfg.bundle.domain; });
134       # This indirection is required to ensure the nix path
135       # gets copied over to the target machine in remote deployments.
136       # Furthermore, it's important that we use exec to
137       # run the binary otherwise we get process leakage due to this
138       # being executed on every change.
139       executable = pkgs.writeShellScript "bundle-wrapper" ''
140         set -e
141         ${cfg.bundle.secretScript}
142         set -xe
143         ${cfg.bundle.publicScript}
144         exec ${cfg.bundle.executable}
145       '';
147     in
148     {
149       systemd.services.keter = {
150         description = "keter app loader";
151         script = ''
152           set -xe
153           mkdir -p ${incoming}
154           ${lib.getExe cfg.package} ${globalKeterConfigFile};
155         '';
156         wantedBy = [ "multi-user.target" "nginx.service" ];
158         serviceConfig = {
159           Restart = "always";
160           RestartSec = "10s";
161         };
163         after = [
164           "network.target"
165           "local-fs.target"
166           "postgresql.service"
167         ];
168       };
170       # On deploy this will load our app, by moving it into the incoming dir
171       # If the bundle content changes, this will run again.
172       # Because the bundle content contains the nix path to the executable,
173       # we inherit nix based cache busting.
174       systemd.services.load-keter-bundle = {
175         description = "load keter bundle into incoming folder";
176         after = [ "keter.service" ];
177         wantedBy = [ "multi-user.target" ];
178         # we can't override keter bundles because it'll stop the previous app
179         # https://github.com/snoyberg/keter#deploying
180         script = ''
181           set -xe
182           cp ${bundle}/bundle.tar.gz.keter ${incoming}/${cfg.bundle.appName}.keter
183         '';
184         path = [
185           executable
186           cfg.bundle.executable
187         ]; # this is a hack to get the executable copied over to the machine.
188       };
189     }
190   );