nix-deamon fails to start after clean install on macOS #77

Open
opened 2026-04-19 15:40:54 +00:00 by ifreilicht · 3 comments
Member

I uninstalled my previous installation of nix successfully and ran the current version of the lix-installer via curl. The installation itself was successful, the nix command is in my PATH, but the daemon doesn't start automatically. I can start it manually, but that's suboptimal.

Error

Running /nix/nix-installer self-test

Error: 
   0: Self test error, install may be only partially functional
      Shell `sh` failed self-test with command `"sh" "-lc" "exec nix build --option substitute false --option post-build-hook '' --no-link --expr 'derivation { name = \"self-test-sh-1776608848767\"; system = \"aarch64-darwin\"; builder = \"/bin/sh\"; args = [\"-c\" \"echo hello > \\$out\"]; }'"`, stderr:
      error:
             … while evaluating an expression to select 'drvPath' on it
               at «internal»:1:552:
             … while evaluating strict
               at «internal»:1:552:
             (stack trace truncated; use '--show-trace' to show the full trace)

             error: could not connect to any lix socket (tried /nix/var/nix/daemon-socket/socket)
I uninstalled my previous installation of nix successfully and ran the current version of the lix-installer via curl. The installation itself was successful, the nix command is in my PATH, but the daemon doesn't start automatically. I can start it manually, but that's suboptimal. ## Error Running `/nix/nix-installer self-test` ``` Error: 0: Self test error, install may be only partially functional Shell `sh` failed self-test with command `"sh" "-lc" "exec nix build --option substitute false --option post-build-hook '' --no-link --expr 'derivation { name = \"self-test-sh-1776608848767\"; system = \"aarch64-darwin\"; builder = \"/bin/sh\"; args = [\"-c\" \"echo hello > \\$out\"]; }'"`, stderr: error: … while evaluating an expression to select 'drvPath' on it at «internal»:1:552: … while evaluating strict at «internal»:1:552: (stack trace truncated; use '--show-trace' to show the full trace) error: could not connect to any lix socket (tried /nix/var/nix/daemon-socket/socket) ```
ifreilicht changed title from Installer sets up LaunchDaemon on macOS improperly, causing nix-deamon to not start to nix-deamon fails to start after clean install on macOS 2026-04-19 18:21:46 +00:00
Author
Member

Okay, I found what the issue was; the smoking gun was in the system logs:

$ log show --last 5m | grep -i nix-daemon
[...]
2026-04-19 17:41:52.545967+0200 0x129c     Default     0x22dd               588    0    backgroundtaskmanagementd: [com.apple.backgroundtaskmanagement:main] registerLaunchItem: found existing item: uuid=3CF694BA-88A4-4303-B22B-6B4A0DA27470, name=sh, type=legacy daemon, disposition=[enabled, disallowed, notified], identifier=16.org.nixos.nix-daemon, url=file:///Library/LaunchDaemons/org.nixos.nix-daemon.plist
[...]

disposition=[enabled, disallowed, notified] means that the Launch Item has been enabled but not whitelisted by the user. I don't know why that happened, but it can be adjusted in the settings UI, see screenshot. Because the executable of our LaunchDaemon is /bin/sh, that's what it shows as the name and icon. I'll see if anything can be done about these issues.

Okay, I found what the issue was; the smoking gun was in the system logs: ``` $ log show --last 5m | grep -i nix-daemon [...] 2026-04-19 17:41:52.545967+0200 0x129c Default 0x22dd 588 0 backgroundtaskmanagementd: [com.apple.backgroundtaskmanagement:main] registerLaunchItem: found existing item: uuid=3CF694BA-88A4-4303-B22B-6B4A0DA27470, name=sh, type=legacy daemon, disposition=[enabled, disallowed, notified], identifier=16.org.nixos.nix-daemon, url=file:///Library/LaunchDaemons/org.nixos.nix-daemon.plist [...] ``` `disposition=[enabled, disallowed, notified]` means that the Launch Item has been enabled but not whitelisted by the user. I don't know why that happened, but it can be adjusted in the settings UI, see screenshot. Because the executable of our LaunchDaemon is /bin/sh, that's what it shows as the name and icon. I'll see if anything can be done about these issues.
Author
Member

Okay, it seems like this was something that I changed at some point in the past on my machine, not knowing what it was. In theory, these services should be activated by default. Not an issue for regular users. There was a command in the past to change put a service on the allowlist (sfltool add-item), but this was removed for security reasons, so we might have to add something to the troubleshooting docs.

It's unfortunate that the custom service just has the name sh though, and nix-darwin actually has the exact same issue: https://github.com/nix-darwin/nix-darwin/issues/1678

The solution is to change the plist to something like this:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>EnvironmentVariables</key>
	<dict>
		<key>NIX_SSL_CERT_FILE</key>
		<string>/etc/ssl/certs/ca-certificates.crt</string>
		<key>OBJC_DISABLE_INITIALIZE_FORK_SAFETY</key>
		<string>YES</string>
	</dict>
	<key>KeepAlive</key>
	<true/>
	<key>Label</key>
	<string>org.nixos.nix-daemon</string>
	<key>LowPriorityIO</key>
	<false/>
	<key>ProcessType</key>
	<string>Standard</string>
	<key>Program</key>
    <string>/opt/nix/bin/nix-daemon</string>
	<key>SoftResourceLimits</key>
	<dict>
		<key>NumberOfFiles</key>
		<integer>1048576</integer>
	</dict>
</dict>
</plist>

Where /opt/nix/bin/nix-daemon contains the shell-script to delay launching the actual executable from the nix-store until it is mounted:

#!/bin/sh

/bin/wait4path /nix/store && exec /nix/store/1pqspbix7hdl6pm2g1fl4iswzjxphv56-lix-2.95.1/bin/nix-daemon
Okay, it seems like this was something that I changed at some point in the past on my machine, not knowing what it was. In theory, these services should be activated by default. Not an issue for regular users. There was a command in the past to change put a service on the allowlist (`sfltool add-item`), but this was removed for security reasons, so we might have to add something to the troubleshooting docs. It's unfortunate that the custom service just has the name `sh` though, and `nix-darwin` actually has the exact same issue: https://github.com/nix-darwin/nix-darwin/issues/1678 The solution is to change the plist to something like this: ```xml <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>EnvironmentVariables</key> <dict> <key>NIX_SSL_CERT_FILE</key> <string>/etc/ssl/certs/ca-certificates.crt</string> <key>OBJC_DISABLE_INITIALIZE_FORK_SAFETY</key> <string>YES</string> </dict> <key>KeepAlive</key> <true/> <key>Label</key> <string>org.nixos.nix-daemon</string> <key>LowPriorityIO</key> <false/> <key>ProcessType</key> <string>Standard</string> <key>Program</key> <string>/opt/nix/bin/nix-daemon</string> <key>SoftResourceLimits</key> <dict> <key>NumberOfFiles</key> <integer>1048576</integer> </dict> </dict> </plist> ``` Where `/opt/nix/bin/nix-daemon` contains the shell-script to delay launching the actual executable from the nix-store until it is mounted: ```sh #!/bin/sh /bin/wait4path /nix/store && exec /nix/store/1pqspbix7hdl6pm2g1fl4iswzjxphv56-lix-2.95.1/bin/nix-daemon ```
Member

There's actually a solution for this class of issues already implemented in the installer, but not currently enabled by default. That's the "daemon stub" that the installer has the ability to download and install; it's a static binary we build that finds the right daemon and execs it.

Certain hardened mac setups have an issue where they require explicit user permission for {launch daemons, access to the full disk} via a popup on first exec, but three annoying things happen:

  • The macOS permissions database identifies the process either by app ID (for an app that goes through the 'proper' signing process used for things like app store apps) or by hash of the binary. This means that you wind up requiring a new permissions entry every time you upgrade lix (and the daemon's hash changes), and the older permissions entries hang around.
  • As a corollary to the previous issue, giving the shell access to permissions like "full disk" punches a huge hole in the user's security model, which is something we really don't want to do, so this very much has to be targeting a specific file.
  • Most irritatingly, a launch daemon isn't connected to the login session; and thus can't display the relevant request popups. So, macOS just adds the "allow this?" toggle to the relevant places in settings, but doesn't enable it. Equally annoyingly, if the user runs the daemon manually from their login session, macOS actually remembers that the daemon tried to get permissions and didn't succeed, so it assumes it was rejected in the past and won't raise the popup for full disk access on machines thaf require it anyway.

The solution I came up with for this was the "stub daemon" (see the bin folder in the installer), which is a small binary designed to remain unchanged after install, which then loads the main daemon from either the system or default nix profile.

Right now, we only install the stub daemon when we know the user is going to run into the "full disk access" issue; but we can turn it on for the general case.

The issue right now is that nix-darwin replaces the launchd configuration each time anyway, replacing the command with a hardcoded path to the daemon. We need to work with the nix-darwin maintainer to have that not happen if the stub daemon is present, but I haven't gotten around to making a case for it, yet.

(Given this new case, it prolly makes sense to add the wait-for-store into the stub daemon as well; it wasn't necesarry for the full disk access case.)

In case it helps anyone in the interim, the Nix config to set the daemon command on nix-darwin to e.g. your shell script is:

launchd.daemons.nix-daemon.command = "<your script path here>";

You can then point your script to /nix/var/nix/profiles/system/sw/bin/nix-daemon instead of a specific daemon, which should always be the most recent daemon installed by nix-darwin.

Hope that helps, and thanks for the report!

There's actually a solution for this class of issues already implemented in the installer, but not currently enabled by default. That's the "daemon stub" that the installer has the ability to download and install; it's a static binary we build that finds the right daemon and `exec`s it. Certain hardened mac setups have an issue where they require explicit user permission for {launch daemons, access to the full disk} via a popup on first exec, but three annoying things happen: - The macOS permissions database identifies the process either by app ID (for an app that goes through the 'proper' signing process used for things like app store apps) or by hash of the binary. This means that you wind up requiring a new permissions entry _every time you upgrade lix_ (and the daemon's hash changes), and the older permissions entries hang around. - As a corollary to the previous issue, giving the _shell_ access to permissions like "full disk" punches a huge hole in the user's security model, which is something we really don't want to do, so this very much has to be targeting a specific file. - **Most irritatingly, a launch daemon isn't connected to the login session; and thus can't display the relevant request popups.** So, macOS just adds the "allow this?" toggle to the relevant places in settings, but doesn't enable it. Equally annoyingly, if the user runs the daemon manually from their login session, macOS actually remembers that the daemon tried to get permissions and didn't succeed, so it assumes it was rejected in the past and won't raise the popup for full disk access on machines thaf require it anyway. The solution I came up with for this was the "stub daemon" (see the bin folder in the installer), which is a small binary designed to remain unchanged after install, which then loads the main daemon from either the `system` or `default` nix profile. Right now, we only install the stub daemon when we know the user is going to run into the "full disk access" issue; but we can turn it on for the general case. The issue right now is that `nix-darwin` replaces the launchd configuration each time _anyway_, replacing the `command` with a hardcoded path to the daemon. We need to work with the nix-darwin maintainer to have that not happen if the stub daemon is present, but I haven't gotten around to making a case for it, yet. (Given this new case, it prolly makes sense to add the wait-for-store into the stub daemon as well; it wasn't necesarry for the full disk access case.) In case it helps anyone in the interim, the Nix config to set the daemon command on `nix-darwin` to e.g. your shell script is: ``` launchd.daemons.nix-daemon.command = "<your script path here>"; ``` You can then point your script to `/nix/var/nix/profiles/system/sw/bin/nix-daemon` instead of a specific daemon, which should always be the most recent daemon installed by nix-darwin. Hope that helps, and thanks for the report!
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
lix-project/lix-installer#77
No description provided.