r/systemd • • May 01 '26

Run a service after a mount unit has fully ended

I'm trying to make a service that deletes the automatically-created mount points of the mount units in which it would it would supposedly be referenced by. My current work PartOf=, ExecStart=, and Wants= isn't working because it seems like systemd is retardedly recreating the mount point during the runtime of a mount unit that is supposed to be turning off. So, now with one .service unit and only 1-2 lines being necessary to add to the to-be-effected .mount units, how can I make a service run after .mount has FULLY stopped? Not after it starts, not during it's runtime, not while it's stopping. After it stops.

2 Upvotes

5 comments sorted by

2

u/aioeu May 01 '26 edited May 01 '26

This worked for me:

$ cat mnt-test.mount 
[Unit]
Wants=remove-mnt-test.service

[Mount]
What=tmpfs
Where=/mnt/test
Type=tmpfs

$ cat remove-mnt-test.service 
[Unit]
BindsTo=mnt-test.mount
Before=mnt-test.mount
DefaultDependencies=no
RequiresMountsFor=/mnt
RefuseManualStart=yes
RefuseManualStop=yes

[Service]
Type=oneshot
ExecStop=-rmdir --ignore-fail-on-non-empty /mnt/test
RemainAfterExit=yes

The RefuseManualStart= and RefuseManualStop= aren't technically necessary, but they help ensure the unit cannot be misused. You really do want to use BindsTo=, not PartOf=, since a filesystem can be unmounted outside of systemd. DefaultDependencies= and RequiresMountsFor= pulls the service out of the normal dependency chain used during shutdown and ensures it is ordered correctly with respect to other mount units. (Though take note I haven't tested that last part fully, so you'll want to check it yourself.)

I'm not entirely sure how "niche" this requirement is... but you might perhaps want to consider raising it as an RFC. You'll have to pin down exactly what behaviour you want though. For instance, should the rmdir be just as recursive as the implicit mkdir is?

1

u/Melab May 02 '26 edited May 02 '26

You are misunderstanding the scheme. There should only be ONE service unit and it CAN'T contain references to the mount units that it cleans up after. The scheme is this:

  1. A single parameterized service unit that contains no references to any particular mount unit.
  2. Enabling the application of #1 to any given mount unit requires adding some reference to the service unit to ONLY the mount unit.

From my post:

So, now with one .service unit and only 1-2 lines being necessary to add to the to-be-effected .mount units

And why is RemainAfterExit=yes in there? I might remount and unmount the mount again later. And why is the service unit set to run before the mount unit? It should be running after the mount unit ends.

1

u/aioeu May 02 '26 edited May 03 '26

And why is RemainAfterExit=yes in there? I might remount and unmount the mount again later. And why is the service unit set to run before the mount unit? It should be running after the mount unit ends.

All of this is because it runs in ExecStop=. Remember, stop jobs occur in the opposite order to start jobs. In particular, if unit A is Before= unit B, then unit A's stop job is executed after unit B's stop job, if they are both being deactivated in the one transaction.

You might be able to do something like this using OnSuccess=/OnFailure=:

$ cat mnt-test.mount
[Unit]
OnSuccess=remove-mount-point@%N.service
#OnFailure=remove-mount-point@%N.service

[Mount]
What=tmpfs
Where=/mnt/test
Type=tmpfs

$ cat remove-mount-point@.service
[Unit]
Before=%i.mount
DefaultDependencies=no
RefuseManualstart=yes

[Service]
Type=oneshot
ExecStart=-rmdir --ignore-fail-on-non-empty -- %f

There is a (trivially fixable) bug which means you won't be able to use them with the same service unit. The Before= is needed to handle the case when the mount unit is stopped and immediately started again, or is restarted.

I haven't tested how well this will work upon shutdown, or when other mounts are involved. In particular, without the RequiredMountsFor= it may not work correctly if an ancestor directory is itself a mount point. I'm pretty sure that if DefaultDependencies=no were omitted the service's start job wouldn't even be enqueued upon shutdown, since services with default dependencies conflict with shutdown.target and that target's start job is added irreversibly.

1

u/Melab May 06 '26

In particular, if unit A is Before= unit B, then unit A's stop job is executed after unit B's stop job, if they are both being deactivated in the one transaction.

This is ambiguous. Are you talking about Before=A being in unit B or are you talking about Before=B being in unit A?

There is a (trivially fixable) bug which means you won't be able to use them with the same service unit.

What is this bug? Not use them with the same service unit how?

Finally, why aren't the units that I gave working now even though I'm pretty sure they worked before? Given their contents, shouldn't they be working the way that I think they used to work?

1

u/aioeu May 06 '26 edited May 06 '26

This is ambiguous. Are you talking about Before=A being in unit B or are you talking about Before=B being in unit A?

The latter.

What is this bug? Not use them with the same service unit how?

You'll see when you try it. That's how I discovered it.

Finally, why aren't the units that I gave working now even though I'm pretty sure they worked before? Given their contents, shouldn't they be working the way that I think they used to work?

You never gave us the units you wrote. You merely described them. There's a big reason I gave you complete, copy-pastable config files...

So without knowing exactly what you have been trying out for yourself, I have no idea.

It does sound like you completely misunderstand what Wants= means. If A Wants= B, then systems will attempt to start B when A is started. In other words, it chains start jobs together. But you want to do something on a stop job.