Ahh, yea actually that’s a good point I hadn’t realized about this workaround, and that’s something we should probably be able to issue a build failure / warning about: When something is a dependency of initrd-switch-root.target, it can cause pretty extreme confusion about what happens with units in stage 2, because the stage 1 unit can actually survive into stage 2 and take its place.
In this case, plymouth-start.service has WantedBy=initrd-switch-root.target, which allows the unit state that systemd keeps track of to persist from stage 1 to stage 2. For plymouth-start.service, this is correct, because plymouth is meant to continue running across the transition from stage 1 to 2. So when stage 2 starts, it sees that plymouth-start.service is already active, and it doesn’t queue a new plymouth-start.service/start job. Since plymouth is already running, that’s correct.
But that would not be correct for systemd-modules-load.service. If your /etc/modules-load.d/ differs between stage 1 and stage 2 (which is very normal), you need this service to start again in stage 2. But since it has RemainAfterExit=yes, it needs to be stopped before transition, or else it’ll be active at transition, and stage 2 systemd will think it doesn’t need to be started again. The way it’s usually stopped is stage 1’s initrd-cleanup.service, which does systemctl isolate initrd-switch-root.target; the isolate command says to stop everything except for the dependencies of the specified unit, so that’s what normally stops systemd-modules-load.service before transition.
If you add boot.initrd.systemd.services.plymouth-start.requires = [ "systemd-modules-load.service" ];, that makes systemd-modules-load.service a transitive dependency of initrd-switch-root.target, which means it will survive transition and it won’t start again in stage 2. There’s an even worse version of this problem, where you accidentally make sysinit.target a dependency of initrd-switch-root.target if you add your own service to initrd-switch-root.target’s dependencies and don’t have DefaultDependencies=no to break the default dependency on sysinit.target; in that case, sysinit.target begins stage 2 already active, which breaks all sorts of unit ordering rules in stage 2 and becomes extremely confusing.
We should probably add logic to detect these errant dependencies and fail the build if you have them, because it’s extremely unusual for a unit surviving the stage 2 transition to be intended, and the consequences can be awfully hard to diagnose. Then we can just have an option like boot.initrd.systemd.<type>.<name>.allowStage2Persistence to explicitly permit the units that we know should do this, like initrd-fs.target and plymouth-start.service.