I did not expect to write such a long answer, but you seem to have been trying different things with systemd, and I assume that you are more interested in its machinery than just simply typing a magic command once and forgetting that it existed. And I can talk about systemd much, much longer!
Stephen Kitt's answer is very good and exhaustive from the administration point of view (and I am gladly up-voting it), when you as an administrator want to disable a service from starting or being enabled. As systemd documentation v241 says (see
man 5 systemd.unit):
If a unit file is empty (i.e. has the file size 0) or is symlinked to
/dev/null, its configuration will not be loaded and it appears with a load state of "masked", and cannot be activated. Use this as an effective way to fully disable a unit, making it impossible to start it even manually.
systemctl mask does exactly that: it creates a symlink with the same name as the unit under
/etc and symlinks the service file to
/dev/null. Since systemd looks under
/etc first (the order is given in the same manual, very first lines), and finds the named unit, it discovers that the unit is masked. It was designed for that, and please use it, if you want to prevent a service from being both enabled and started. It's the simplest, a single-command solution that survives a reboot. Keep simple what can be done simple.
However, if you are designing a set of services that depend on each other, in the role of a system builder rather than administrator, it makes sense to declare the service unstartable or unstoppable with the
systemctl command, while still allowing other mechanisms (sockets, dbus etc.) to start it on demand. For this purpose, systemd has settings, also described in the same manual:
Takes a boolean argument. If true, this unit can only be activated or deactivated indirectly. In this case, explicit start-up or termination requested by the user is denied, however if it is started or stopped as a dependency of another unit, start-up or termination will succeed. This is mostly a safety feature to ensure that the user does not accidentally activate units that are not intended to be activated explicitly, and not accidentally deactivate units that are not intended to be deactivated. These options default to false.
You can enable such a service, but it will refuse to start with
systemctl start (or stop, for the other setting), preventing an operator error.
A point to elaborate on, suggested by your idea of commenting the
[Install] section in the service file:
Do not do that if the service come with the system, that is, when the unit file lives somewhere under the
/lib hierarchy! An upgrade will either silently overwrite it, or ask you to manually intervene to merge changes. systemd has a better mechanism for that. By convention (for system services), the systemd's directory under
/etc is "yours", but one under
/lib belongs to the distro.
If, for example, you wanted to remove the
WantedBy= clause from the
[Install] section, you should use the systemd proper mechanism for applying little tweak to the services: use the command
sudo systemctl edit servicename.service. systemd will open an editor for you, and initially the file will be empty.
The next useful property of systemd that comes handy is that when a keyword value is a list (such as
WantedBy), assigning it an empty string resets the list (there are inconsistencies in different versions, but most of the time it works. In our case it does). In the editor, you add two lines (this is only an elaboration of the concept that you tried but did not quite reach a working solution; we already know that you'll achieve what you want by masking):
This resets the
WantedBy list to empty. If you want to add another dependency, simply put another line with the same key (another
WantedBy=another.service, in our ill-conceived example). What actually happens behind the scenes, systemd creates an override file under
/etc, i. e. in your, machine owner's, directory, and promises to never touch it. The file that systemd creates is named, assuming default directory layout,
/etc/systemd/system/servicename.service.d/override.conf. Here the part servicename.service is exactly the unit name you are overriding. systemd respects all files in this new subdirectory, so there can be more than just
override.conf; only mind that they will be applied in lexicographical order of names, lowest first (as
ls -1 *.conf | LC_ALL=C sort would show them).
While not a correct mechanism in your case, as Peter already answered in a follow-up comment, sometimes it is helpful when you want, for example, to add an environment variable, or even work around a bug¹, like
$ cat /etc/systemd/system/systemd-remount-fs.service.d/override.conf
# Workaround for the systemd bug #14603: remount-fs and growfs@
# race, so that the latter may fail because the root fs is still
# readonly. This dependency ensures that the remount happens first.
You can verify that your override was incorporated into the unit configuration using
systemctl show myunit.service . It's output is very verbose, so use grep:
$ systemctl show systemd-remount-fs.service | grep ^Before=
Before=systemd-update-utmp.service [...] systemd-growfs@-.service [...]
Not the clearest example in the world to read, as the line is very long; I excised most of it where the
[...] markers are, but you can spot
systemd-growfs@-.service in the list. It's a good practice to always verify that the configuration change really made it into the combined settings of the unit even before trying different start/stop/boot/hibernate and other scenarios. Misspellings happen.
Naturally, you can tweak any type of units, not only services: timers, sockets, mounts etc. Even the unit files dynamically generated under the
/run directory respect these tweaks. For example, although in the following case the
/opt mount is mounted using your regular
/etc/fstab, systemd generates a temporary
opt.mount unit for it in the in-RAM filesystem directory
/run/systemd/system. This temporary unit perfectly recognizes an additional
Wants= specified in
/etc/systemd/system. And, by the way, the service that it Wants is an example of correct use of
RefuseManualStop: it is supposed to do its job only upon
/opt filesystem mount and umount, and would hang or spit errors if started manually. Even worse, it wouldn't start just after the
/opt filesystem had been mounted, had it been manually started already!
The service also shows the "strong binding" pattern by specifying both
After=opt.mount. Quoting the systemd.unit(5) manual, under "[UNIT] SECTION OPTIONS/BindsTo=", with my additions in brackets to make the language more digestible:
When used in conjunction with After= on the same unit[,] the behaviour of BindsTo= is even stronger. In this case, the unit [which this unit is] bound to [must] be in active state for this unit to also be [and remain] in active state.
The implication here is that this unit is started after and stopped before
opt.mount is stopped, in a tight lockstep.
* * *
systemd is a large, powerful and flexible system, and, if you are after tuning your system the best way possible, reading its documentation in
man or on the Web when you get bored with other things is certainly beneficial.
¹ The bug was fixed more than a year ago, and the fix made its way into the version 245 of systemd, but since I'm herding a fleet of various distributions and sometimes rolling up VMs from older images, I've got to keep it for a while. Debian 10 has a pre-fix v241 of systemd; only Debian 11 comes with v247. Production systems are upgraded slowly, so the fix is to stay around for quite a few years more.