From e506f4feb927374c8322ae4c77e9a66b2676a485 Mon Sep 17 00:00:00 2001 From: Luana Date: Fri, 2 Jan 2026 12:52:47 -0300 Subject: [PATCH 1/3] nixos/actual: Refactor dataDir so it's user-modifiable --- nixos/modules/services/web-apps/actual.nix | 40 ++++++++++++++++++---- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/nixos/modules/services/web-apps/actual.nix b/nixos/modules/services/web-apps/actual.nix index 0e0bd29c4236..29180036af9f 100644 --- a/nixos/modules/services/web-apps/actual.nix +++ b/nixos/modules/services/web-apps/actual.nix @@ -17,7 +17,6 @@ let ; cfg = config.services.actual; - dataDir = "/var/lib/actual"; formatType = pkgs.formats.json { }; in @@ -53,12 +52,34 @@ in description = "The port to listen on"; default = 3000; }; - }; - config = { - serverFiles = mkDefault "${dataDir}/server-files"; - userFiles = mkDefault "${dataDir}/user-files"; - dataDir = mkDefault dataDir; + dataDir = lib.mkOption { + type = lib.types.str; + default = "/var/lib/actual"; + description = '' + Directory under which Actual runs and saves its data. + + Changing this after you already have a working instance may make Actual fail to start, even if you move all files in the data dir. If migration is needed, refer to [this comment](https://github.com/actualbudget/actual/issues/3957#issuecomment-2567076794) for a fix. + ''; + }; + + serverFiles = lib.mkOption { + type = lib.types.str; + default = "${cfg.settings.dataDir}/server-files"; + defaultText = "\${cfg.settings.dataDir}/server-files"; + description = '' + The server will put an account.sqlite file in this directory, which will contain the (hashed) server password, a list of all the budget files the server knows about, and the active session token (along with anything else the server may want to store in the future). + ''; + }; + + userFiles = lib.mkOption { + type = lib.types.str; + default = "${cfg.settings.dataDir}/user-files"; + defaultText = "\${cfg.settings.dataDir}/user-files"; + description = '' + The server will put all the budget files in this directory as binary blobs. + ''; + }; }; }; }; @@ -85,7 +106,7 @@ in Group = "actual"; StateDirectory = "actual"; RuntimeDirectory = "actual"; - WorkingDirectory = dataDir; + WorkingDirectory = cfg.settings.dataDir; LimitNOFILE = "1048576"; PrivateTmp = true; PrivateDevices = true; @@ -107,6 +128,11 @@ in ProtectProc = "invisible"; ProcSubset = "pid"; ProtectSystem = "strict"; + ReadWritePaths = [ + cfg.settings.dataDir + cfg.settings.serverFiles + cfg.settings.userFiles + ]; RestrictAddressFamilies = [ "AF_INET" "AF_INET6" From 3a535dc67f4e2f8d18eb33332aa96315b5512f88 Mon Sep 17 00:00:00 2001 From: Luana Date: Fri, 2 Jan 2026 19:17:17 -0300 Subject: [PATCH 2/3] nixos/actual: Allow for user and group setting --- nixos/modules/services/web-apps/actual.nix | 39 +++++++++++++++++++--- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/nixos/modules/services/web-apps/actual.nix b/nixos/modules/services/web-apps/actual.nix index 29180036af9f..f51b0c9a0dba 100644 --- a/nixos/modules/services/web-apps/actual.nix +++ b/nixos/modules/services/web-apps/actual.nix @@ -31,6 +31,26 @@ in description = "Whether to open the firewall for the specified port."; }; + user = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + description = '' + User account under which Actual runs. + + If null is specified (default), a temporary user will be created by systemd. Otherwise won't be automatically created by the service. + ''; + }; + + group = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + description = '' + Group account under which Actual runs. + + If null is specified (default), a temporary user will be created by systemd. Otherwise won't be automatically created by the service. + ''; + }; + settings = mkOption { default = { }; description = '' @@ -101,9 +121,6 @@ in serviceConfig = { ExecStart = getExe cfg.package; - DynamicUser = true; - User = "actual"; - Group = "actual"; StateDirectory = "actual"; RuntimeDirectory = "actual"; WorkingDirectory = cfg.settings.dataDir; @@ -146,7 +163,21 @@ in "@pkey" ]; UMask = "0077"; - }; + } + // ( + if cfg.user != null then + { + DynamicUser = false; + Group = cfg.group; + User = cfg.user; + } + else + { + DynamicUser = true; + User = "actual"; + Group = "actual"; + } + ); }; }; From f884556f2c4c6343e70b7b8b44162a70d7bc0d76 Mon Sep 17 00:00:00 2001 From: Luana Date: Sun, 4 Jan 2026 17:02:08 -0300 Subject: [PATCH 3/3] nixos/actual: Add tests for user, group, port and dataDir setting --- nixos/tests/actual.nix | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/nixos/tests/actual.nix b/nixos/tests/actual.nix index 26ddcb3d236e..77020fc6c47c 100644 --- a/nixos/tests/actual.nix +++ b/nixos/tests/actual.nix @@ -9,8 +9,47 @@ services.actual.enable = true; }; + nodes.machine2 = + { ... }: + { + services.actual = { + enable = true; + user = "actual"; + group = "actual"; + settings = { + port = 7000; + dataDir = "/var/lib/actual-test"; + }; + }; + + users.users.actual = { + group = "actual"; + home = "/var/lib/actual-test"; + isSystemUser = true; + }; + + users.groups.actual = { }; + + systemd.tmpfiles.settings = { + "10-actualdir" = { + "/var/lib/actual-test" = { + d = { + group = "actual"; + mode = "0755"; + user = "actual"; + }; + }; + }; + }; + }; + testScript = '' + start_all() + machine.wait_for_open_port(3000) machine.succeed("curl -fvvv -Ls http://localhost:3000/ | grep 'Actual'") + + machine2.wait_for_open_port(7000) + machine2.succeed("curl -fvvv -Ls http://localhost:7000/ | grep 'Actual'") ''; }