1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6 cfg = config.services.ntfy-sh;
7
8 settingsFormat = pkgs.formats.yaml { };
9in
10
11{
12 options.services.ntfy-sh = {
13 enable = mkEnableOption "[ntfy-sh](https://ntfy.sh), a push notification service";
14
15 package = mkPackageOption pkgs "ntfy-sh" { };
16
17 user = mkOption {
18 default = "ntfy-sh";
19 type = types.str;
20 description = "User the ntfy-sh server runs under.";
21 };
22
23 group = mkOption {
24 default = "ntfy-sh";
25 type = types.str;
26 description = "Primary group of ntfy-sh user.";
27 };
28
29 settings = mkOption {
30 type = types.submodule {
31 freeformType = settingsFormat.type;
32 options = {
33 base-url = mkOption {
34 type = types.str;
35 example = "https://ntfy.example";
36 description = ''
37 Public facing base URL of the service
38
39 This setting is required for any of the following features:
40 - attachments (to return a download URL)
41 - e-mail sending (for the topic URL in the email footer)
42 - iOS push notifications for self-hosted servers
43 (to calculate the Firebase poll_request topic)
44 - Matrix Push Gateway (to validate that the pushkey is correct)
45 '';
46 };
47 };
48 };
49
50 default = { };
51
52 example = literalExpression ''
53 {
54 listen-http = ":8080";
55 }
56 '';
57
58 description = ''
59 Configuration for ntfy.sh, supported values are [here](https://ntfy.sh/docs/config/#config-options).
60 '';
61 };
62 };
63
64 config =
65 let
66 configuration = settingsFormat.generate "server.yml" cfg.settings;
67 in
68 mkIf cfg.enable {
69 # to configure access control via the cli
70 environment = {
71 etc."ntfy/server.yml".source = configuration;
72 systemPackages = [ cfg.package ];
73 };
74
75 services.ntfy-sh.settings = {
76 auth-file = mkDefault "/var/lib/ntfy-sh/user.db";
77 listen-http = mkDefault "127.0.0.1:2586";
78 attachment-cache-dir = mkDefault "/var/lib/ntfy-sh/attachments";
79 cache-file = mkDefault "/var/lib/ntfy-sh/cache-file.db";
80 };
81
82 systemd.services.ntfy-sh = {
83 description = "Push notifications server";
84
85 wantedBy = [ "multi-user.target" ];
86 after = [ "network.target" ];
87
88 serviceConfig = {
89 ExecStart = "${cfg.package}/bin/ntfy serve -c ${configuration}";
90 User = cfg.user;
91 StateDirectory = "ntfy-sh";
92
93 DynamicUser = true;
94 AmbientCapabilities = "CAP_NET_BIND_SERVICE";
95 PrivateTmp = true;
96 NoNewPrivileges = true;
97 CapabilityBoundingSet = "CAP_NET_BIND_SERVICE";
98 ProtectSystem = "full";
99 ProtectKernelTunables = true;
100 ProtectKernelModules = true;
101 ProtectKernelLogs = true;
102 ProtectControlGroups = true;
103 PrivateDevices = true;
104 RestrictSUIDSGID = true;
105 RestrictNamespaces = true;
106 RestrictRealtime = true;
107 MemoryDenyWriteExecute = true;
108 # Upstream Recommandation
109 LimitNOFILE = 20500;
110 };
111 };
112
113 users.groups = optionalAttrs (cfg.group == "ntfy-sh") {
114 ntfy-sh = { };
115 };
116
117 users.users = optionalAttrs (cfg.user == "ntfy-sh") {
118 ntfy-sh = {
119 isSystemUser = true;
120 group = cfg.group;
121 };
122 };
123 };
124}