1 { config, pkgs, lib, ... }:
6 cfg = config.services.etebase-server;
8 pythonEnv = pkgs.python3.withPackages (ps: with ps;
9 [ etebase-server daphne ]);
11 iniFmt = pkgs.formats.ini {};
13 configIni = iniFmt.generate "etebase-server.ini" cfg.settings;
15 defaultUser = "etebase-server";
19 (mkRemovedOptionModule
20 [ "services" "etebase-server" "customIni" ]
21 "Set the option `services.etebase-server.settings' instead.")
22 (mkRemovedOptionModule
23 [ "services" "etebase-server" "database" ]
24 "Set the option `services.etebase-server.settings.database' instead.")
25 (mkRenamedOptionModule
26 [ "services" "etebase-server" "secretFile" ]
27 [ "services" "etebase-server" "settings" "secret_file" ])
28 (mkRenamedOptionModule
29 [ "services" "etebase-server" "host" ]
30 [ "services" "etebase-server" "settings" "allowed_hosts" "allowed_host1" ])
34 services.etebase-server = {
39 description = lib.mdDoc ''
40 Whether to enable the Etebase server.
42 Once enabled you need to create an admin user by invoking the
43 shell command `etebase-server createsuperuser` with
44 the user specified by the `user` option or a superuser.
45 Then you can login and create accounts on your-etebase-server.com/admin
51 default = "/var/lib/etebase-server";
52 description = lib.mdDoc "Directory to store the Etebase server data.";
56 type = with types; nullOr port;
58 description = lib.mdDoc "Port to listen on.";
61 openFirewall = mkOption {
64 description = lib.mdDoc ''
65 Whether to open ports in the firewall for the server.
69 unixSocket = mkOption {
70 type = with types; nullOr str;
72 description = lib.mdDoc "The path to the socket to bind to.";
73 example = "/run/etebase-server/etebase-server.sock";
77 type = lib.types.submodule {
78 freeformType = iniFmt.type;
85 description = lib.mdDoc ''
86 Whether to set django's DEBUG flag.
89 secret_file = mkOption {
90 type = with types; nullOr str;
92 description = lib.mdDoc ''
93 The path to a file containing the secret
94 used as django's SECRET_KEY.
97 static_root = mkOption {
99 default = "${cfg.dataDir}/static";
100 defaultText = literalExpression ''"''${config.services.etebase-server.dataDir}/static"'';
101 description = lib.mdDoc "The directory for static files.";
103 media_root = mkOption {
105 default = "${cfg.dataDir}/media";
106 defaultText = literalExpression ''"''${config.services.etebase-server.dataDir}/media"'';
107 description = lib.mdDoc "The media directory.";
111 allowed_host1 = mkOption {
114 example = "localhost";
115 description = lib.mdDoc ''
116 The main host that is allowed access.
122 type = types.enum [ "django.db.backends.sqlite3" "django.db.backends.postgresql" ];
123 default = "django.db.backends.sqlite3";
124 description = lib.mdDoc "The database engine to use.";
128 default = "${cfg.dataDir}/db.sqlite3";
129 defaultText = literalExpression ''"''${config.services.etebase-server.dataDir}/db.sqlite3"'';
130 description = lib.mdDoc "The database name.";
136 description = lib.mdDoc ''
137 Configuration for `etebase-server`. Refer to
138 <https://github.com/etesync/server/blob/master/etebase-server.ini.example>
139 and <https://github.com/etesync/server/wiki>
140 for details on supported values.
145 media_root = "/path/to/media";
148 allowed_host2 = "localhost";
155 default = defaultUser;
156 description = lib.mdDoc "User under which Etebase server runs.";
161 config = mkIf cfg.enable {
163 environment.systemPackages = with pkgs; [
164 (runCommand "etebase-server" {
165 nativeBuildInputs = [ makeWrapper ];
167 makeWrapper ${pythonEnv}/bin/etebase-server \
168 $out/bin/etebase-server \
169 --chdir ${escapeShellArg cfg.dataDir} \
170 --prefix ETEBASE_EASY_CONFIG_PATH : "${configIni}"
174 systemd.tmpfiles.rules = [
175 "d '${cfg.dataDir}' - ${cfg.user} ${config.users.users.${cfg.user}.group} - -"
178 systemd.services.etebase-server = {
179 description = "An Etebase (EteSync 2.0) server";
180 after = [ "network.target" "systemd-tmpfiles-setup.service" ];
181 wantedBy = [ "multi-user.target" ];
185 WorkingDirectory = cfg.dataDir;
188 PYTHONPATH = "${pythonEnv}/${pkgs.python3.sitePackages}";
189 ETEBASE_EASY_CONFIG_PATH = configIni;
192 # Auto-migrate on first run or if the package has changed
193 versionFile="${cfg.dataDir}/src-version"
194 if [[ $(cat "$versionFile" 2>/dev/null) != ${pkgs.etebase-server} ]]; then
195 ${pythonEnv}/bin/etebase-server migrate --no-input
196 ${pythonEnv}/bin/etebase-server collectstatic --no-input --clear
197 echo ${pkgs.etebase-server} > "$versionFile"
202 networking = if cfg.unixSocket != null
203 then "-u ${cfg.unixSocket}"
204 else "-b 0.0.0.0 -p ${toString cfg.port}";
206 cd "${pythonEnv}/lib/etebase-server";
207 ${pythonEnv}/bin/daphne ${networking} \
208 etebase_server.asgi:application
212 users = optionalAttrs (cfg.user == defaultUser) {
213 users.${defaultUser} = {
219 groups.${defaultUser} = {};
222 networking.firewall = mkIf cfg.openFirewall {
223 allowedTCPPorts = [ cfg.port ];