Adding a dbus service to a Nix package

I have managed to write, in my NixOS configuration, a user service for the program pass_secret_service (because upstream does not provide one), and I now want to contribute that to the Nix package. Unfortunately, I have not been able to puzzle out how to do this from the NixOS documentation or from looking at existing packages (partly because there are too many to go through without knowing what I’m looking for). Can anyone guide me through doing this?

There are two facets to this: First, you need to add a D-Bus service to the package and then, make sure that system’s D-Bus can see the service. The first belongs to the domain of Nixpkgs, the second is the responsibility of NixOS.

Adding a service to the package is pretty straightforward – you can just modify the package’s Nix expression to install the service file to the package in postInstall like this:

But just modifying the package is a sort of a cop-out. Ideally, you would work together with upstream to add the service file to the project. They can either keep it in a contrib/ directory for people to install manually, or you can try to modify the build system to install it automatically¹. This has the benefit that we would not have to maintain the service ourselves and other distros could benefit from it as well.

Letting D-Bus know about the program can be done using services.dbus.packages option and you could create a NixOS module (example) that does that. But D-Bus will also be aware of packages containing D-Bus services installed using environment.systemPackages so unless you want to, for example, make it configurable using NixOS, you may be able to do without a module.


For systemd services, the steps are mostly the same, except you need to add the package to systemd.packages (environment.systemPackages will not be enough) so you might want to add an actual NixOS module. Additionally, NixOS has an option for creating services using the module system: systemd.user.services . But I would only use it for overriding services since that is not really upstreamable.


  1. In this case, the build system is setup tools, which has pretty bad support for installing extra files (example) so we would still need to patch the path like this. But it would still be better than doing all the work downstream.
1 Like

Actually I prefer and would suggest to use systemd.$unittype.* over systemd.packages, as you can not use the module system to change units installed through the latter.

Sure you can, the systemd module will put the keys specified through systemd.$unittype.* to an override when you set them on a unit that exists in a package installed through systemd.packages:

2 Likes

That’s a good point about it being better to contribute to upstream. Everything you’ve given here should be enough for me to test my changes before submitting to upstream, except for one complication in this particular case: multiple daemons implement the dbus service named org.freedesktop.secrets. Can there be multiple .service files on the system with a [D-BUS Service] section and the same Name= value, just with different filenames?

IIRC, the point of the D-Bus services is on demand (lazy) activation – if a program tries to access a name on a bus that is not currently bound, the D-Bus daemon will check if there is a registered D-Bus service for that name and execute if it is the case. The executed program will then bind to the the name, at which point the D-Bus daemon will pass it the contents of the message queue. (Still an assumption, I have not actually looked.)

So if multiple services with the same name were allowed, D-Bus daemon would have no way of knowing which one of choose. For that reason, I assume that there can only be a single package for each name installed at one time and it is up to the package manager to prevent a conflict.

Alternately, we could decide to eschew the D-Bus activation and use something like XDG autostart or systemd service to start the service with log-in session. But then it would be running even when not used. Also, when multiple autostarted programs would try to bind the same name on the bus, the result would be non-deterministic. So I would recommend going with the explicit conflict.

1 Like

I have the package building with my changes in a local checkout of the nixpkgs git repository, and I am at the point where I want to install this fork of the package on my system, but I do not want to rebuild my entire system from this repository. Is there a way to pull in only a single package from a different nixpkgs source? I am using a flake for my system configuration, and it looks like I could specify another input named something like nixpkgs-fork with the url attribute pointing to my local checkout, but I cannot figure out how to then reference packages from that input.

I typically just rebase the nixpkgs checkout onto the nixpkgs revision my config repo is pinned to. Then I can just override the input and there will be no rebuilds and I do not have to deal with extra inputs.

If you want to go the way of separate input, you can either put the package into Nixpkgs overlays – then it will be available in pkgs. Or you can use specialArgs parameter of lib.nixosSystem to pass extra arguments to all modules.

1 Like

yourLocalInput.legacyPackages.x86_64-linux.thePackage usually.

1 Like

Why legacyPackages?

Because that’s what nixpkgs exposes.

legacyPackages is allowed to be an arbitrary structure, opposes to packages which does only allow a flat set of derivations.

1 Like

I can’t quite figure out how to adapt your example to my system flake. Mine looks like this right now:

{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-21.11";
    nixpkgs-fork.url = "path:/home/aidan/src/nixos/nixpkgs/";
    home-manager.url = "github:nix-community/home-manager";
  };
  outputs = { self, nixpkgs, nixpkgs-fork, home-manager, blender-bin }: {
    nixosConfigurations.aidaltower = nixpkgs.lib.nixosSystem {
      system = "x86_64-linux";
      modules = [
        ({ config, pkgs, ... }:
          { nixpkgs.overlays = [ blender-bin.overlay ];
            environment.systemPackages = [ pkgs.blender_3_0 ];
          })
        home-manager.nixosModule
        ./configuration.nix
      ];
    };
  };
}

Putting

      specialArgs = {
        inherit inputs;
      };

inside the nixpkgs.lib.nixosSystem block produces an error about inputs being undefined, so it is apparently not in scope there.

You additionally need an at-pattern at the argset of the outputs function:

outputs = { … }@inputs: {
  nixosConfigurations.aidaltower = nixpkgs.lib.nixosSystem {
    # …
    modules = [
      ({pkgs}: {
        _module.args = { inherit inputs; }
        nixpkgs.overlays = [ blender-bin.overlay ];
        environment.systemPackages = [ pkgs.blender_3_0 ];
      }
      home-manager.nixosModule
      ./configuration.nix
    ];
  };
}

I was able to get it working like this:

{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-21.11";
    nixpkgs-fork.url = "path:/home/aidan/src/nixos/nixpkgs/";
    home-manager.url = "github:nix-community/home-manager";
  };
  outputs = { self, nixpkgs, nixpkgs-fork, home-manager, blender-bin }: {
    nixosConfigurations.aidaltower = nixpkgs.lib.nixosSystem {
      system = "x86_64-linux";
      specialArgs = {
        nixpkgs-fork = nixpkgs-fork;
      };
      modules = [
        ({ config, pkgs, ... }:
          { nixpkgs.overlays = [ blender-bin.overlay ];
            environment.systemPackages = [ pkgs.blender_3_0 ];
          })
        home-manager.nixosModule
        ./configuration.nix
      ];
    };
  };
}

And then adding nixpkgs-fork as an argument in configuration.nix, then I can refer to it in configuration.nix like this:

  systemd.packages = with pkgs; [
    nixpkgs-fork.legacyPackages.x86_64-linux.pass-secret-service
  ];

But after a nixos-rebuild with these changes, the systemd service is not available. I then realised that I neglected to actually copy the .service files to $out, so I need to fix that and try again.

I got the systemd service to show up, but it’s not being automatically started by dbus when a program tries to talk to it. @jtojnar Does this look like it should work? https://github.com/aidalgol/nixpkgs/blob/16fe3c7444b08ba61384d60a22f48546696f8cfd/pkgs/applications/misc/pass-secret-service/default.nix

That looks about right to me (though naming the systemd service is confusing to me). Did you add the package it to services.dbus.packages/environment.systemPackages in addition to systemd.packages when installing? Do you see anything in the journal? Do you see the service in d-feet (dfeet package)?

Ah, I had left it out of services.dbus.packages. I added it there and now everything seems to be working as expected. Thanks for your help! Time to make a pull request!

(Edited to add:) I agree that the naming is confusing, but it is a convention I have seen used in just about every example of this type of service pairing, so I thought I should follow suit.