Temporarily disabling a systemd service?

Is there a way for me to temporarily disable a systemd service without rebuilding my configuration?

Specifically, I’m looking to write a script that would disable my laptop’s fingerprint sensor whilst connected to my USB dock (so I don’t have to reach over to the laptop all the time). The simplest way to do this seems like it would be to disable the fprintd unit, but /etc/systemd/system is read-only on NixOS.

Is there a way for me to disable this unit temporarily? Here’s what happens when I try to disable or mask the unit:

# systemctl disable fprintd
Failed to disable unit: File /etc/systemd/system/fprintd.service: Read-only file system
# systemctl mask fprintd
Failed to mask unit: File /etc/systemd/system/fprintd.service already exists and is a symlink to /nix/store/9g03jbxp9p82mc1803zwypmabvsd3cii-fprintd-1.94.2/lib/systemd/system/fprintd.service.

I can stop the unit just fine, but it is started again by dbus when my fingeprint is needed.

I’m on NixOS 23.05 (unstable).

systemd also looks under /run/systemd/system in addition to /etc/systemd/system, so you can just play around with changes in there and then do a systemctl daemon-reload. The mask command just symlinks the unit file to /dev/null, so manually doing something like this (untested) should do the trick:

sudo mkdir -p /run/systemd/system
sudo ln -s /run/systemd/system/fprintd.service /dev/null
sudo systemctl daemon-reload

You could use specialisations with/without the service.

Alternatively, you could see if you can use conditionals which cause a shutdown of the service when certain criteria are met.

Or just use an imperative systemctl start/stop on (dis)connecting the dock.

It seemed like it would work, but it looks like /etc/systemd/system appears earlier in the unit path list than /run/systemd/system, so files in /run can’t override ones from /etc (docs here).

I’ve not heard of specialisations but doing some digging they seem really powerful! Having a “docked” specialisation seems like it would offer up a whole host of things I could reconfigure whilst docked :thinking: I’ll give that a try, thanks!

Sadly I can’t just use systemctl stop as it is started by dbus again when my fingerprint is needed.

So I’ve managed to set up a specialisation but I’m having a hard time removing the fprintd service from systemd.packages.

I’ve tried setting systemd.packages with mkDefault in my root configuration and then overriding it in the specialisation (as suggested on the wiki), but using mkDefault in the root configuration makes my root configuration lose the packages from systemd.packages completely (as if I hadn’t set systemd.packages at all).

A minimal example:

{ config, lib, ... }: {
  specialisation = {
    docked.configuration = {
      systemd.packages = [ ];
  systemd.packages = lib.mkDefault [ pkgs.fprintd ];

If I switch to this configuration, the fprintd unit no longer exists, even without switching to the specialisation. Removing lib.mkDefault makes the unit exist again but then I can’t override it from the specialisation.

Is dbus just starting the service or is it prodding a .socket which then launches the service? Socket units can be stopped too.

There’s only a .service - it seems like this part of fprintd.service is what is causing it to start:


In case you need a completely different solution, consider disabling/blocking the fingerprint hardware when the dock is attached.

usbguard can block or remove a device – you’d just need to attach an activation mechanism based on changing presence of the dock. Although, usbguard may be more complicated than you want to manage for this use case, it’s worth running on a laptop which might ever be attached to high-risk usb devices such as public chargers.

Ah that’s a good idea! It seems like I can do the following to disable the sensor:

sudo sh -c "echo 0 > /sys/devices/pci0000:00/0000:00:14.0/usb1/1-9/authorized"

And to re-enable it:

sudo sh -c "echo 1 > /sys/devices/pci0000:00/0000:00:14.0/usb1/1-9/authorized"

I also need to restart the fprintd service after re-enabling, but that seems to work fine. Thanks!

1 Like

Eugh the device ends up getting re-enabled after coming out of sleep - I guess I either need to use something like usbguard or set up a udev rule to re-disable the device when it disappears/reappears whilst docked.

Or maybe a home-made systemd unit? Can you have systemd activate a unit when coming out of sleep? If so, check if you are docked and take appropriate action for the device.

I’ve found that I can mask fprintd.service using ln -s /dev/null /run/systemd/transient/fprintd.service. I’ve set up a pair of udev rules like so which works perfectly:

  services.udev.extraRules = ''
    # Disable fprintd whilst docked
    ACTION=="add", ENV{PRODUCT}=="2188/5802/101", RUN+="${pkgs.bash}/bin/bash -c \"${pkgs.coreutils}/bin/ln -s /dev/null /run/systemd/transient/fprintd.service; ${pkgs.systemd}/bin/systemctl daemon-reload\""
    ACTION=="remove", ENV{PRODUCT}=="2188/5802/101", RUN+="${pkgs.bash}/bin/bash -c \"${pkgs.coreutils}/bin/rm -f /run/systemd/transient/fprintd.service; ${pkgs.systemd}/bin/systemctl daemon-reload\""

I originally tried matching against the thunderbolt device in the udev rule, and whilst this worked for add, it didn’t work for remove as udev doesn’t seem to have any useful data on the device that could be matched against during removal.

Instead I opted for the USB hub (2188:5802) in my dock (a CalDigit TS4) which seems to work fine.

1 Like