22

I don't want to remove the service, I just want to avoid its start on boot. I still need the option to start it manually later (with the systemctl start <service> command).

I tried to use systemctl disable <service>. It doesn't work, because it removes the service.

There is another possibility. In its service file,

[Install]
#WantedBy=multi-user.target

could be commented out (and then, systemctl daemon-reload). It works in the case of my own services, because their service files was written by me.

However, the service files belonging to distribution, are in /lib/systemd/system. Files in this directory are managed by the OS, i.e. they would be overwritten by updates, other parts of the system might assume that these are unmodified, and so on. Simply editing system files out of the /etc is a bad practice, and I don't want to do that. I don't want to edit configuration files in my /lib.

What to do?

11
  • 2
    use systemctl disable <service>
    – eblock
    May 3, 2019 at 10:35
  • 7
    Why would systemctl disable remove a service? I never had that issue, disabling always worked for me. However, editing drop-in files in /etc/systemd/system is actually the desired way, at least that's what I learned.
    – eblock
    May 3, 2019 at 10:52
  • @eblock Yes, it removes the service. After a systemctl disable <service>, you won't be able to see it in systemctl --all any more. You can enable it again if you wish, and the you can start it, but not this is what I want. I only want it to not start on boot (thus, to remove from the wishlist of multi-user.target). Solving the problem on your way would be a workaround, not the solution. However, if there is no better option, I will need to do that.
    – peterh
    May 3, 2019 at 10:59
  • 1
    Check the output of systemctl disable <service> and the existence of that unit file under /lib. Normally it wouldn't be removed, if it really get removed, then you'll not be able to enable or start it again. systemctl --all doesn't seem to be a valid syntax for systemctl . May 3, 2019 at 11:40
  • @炸鱼薯条德里克 systemctl --all most definitely is a valid command. May 3, 2019 at 11:41

2 Answers 2

31

systemctl disable is the correct way to do this; it still allows starting a unit manually, even if it doesn’t appear in systemctl --all’s output — to list all startable units, you should run systemctl list-unit-files instead. To render a unit un-startable, you need to mask it.

$ sudo systemctl stop unbound
$ sudo systemctl status unbound
● unbound.service - Unbound DNS server
   Loaded: loaded (/lib/systemd/system/unbound.service; enabled; vendor preset: enabled)
   Active: inactive (dead) since Fri 2019-05-03 13:12:41 CEST; 5s ago
     Docs: man:unbound(8)
 Main PID: 5320 (code=exited, status=0/SUCCESS)

$ sudo systemctl disable unbound
$ sudo systemctl status unbound
● unbound.service - Unbound DNS server
   Loaded: loaded (/lib/systemd/system/unbound.service; disabled; vendor preset: enabled)
   Active: inactive (dead)
     Docs: man:unbound(8)

$ sudo systemctl start unbound
$ sudo systemctl status unbound
● unbound.service - Unbound DNS server
   Loaded: loaded (/lib/systemd/system/unbound.service; disabled; vendor preset: enabled)
   Active: active (running) since Fri 2019-05-03 13:13:14 CEST; 1s ago
     Docs: man:unbound(8)
  Process: 30513 ExecStartPre=/usr/lib/unbound/package-helper chroot_setup (code=exited, status=0/SUCCESS)
  Process: 30518 ExecStartPre=/usr/lib/unbound/package-helper root_trust_anchor_update (code=exited, status=0/SUCCESS)
 Main PID: 30525 (unbound)
    Tasks: 1 (limit: 4915)
   CGroup: /system.slice/unbound.service
           └─30525 /usr/sbin/unbound -d

If you really want to, you can override system-provided services defined in /lib by adding files in /etc, and change their desired target; systemctl edit yourunit will do the right thing: it opens an editor, allowing you to override only the settings you care about, and it will store the result in the right place, as an override “snippet”. Updates made to non-overridden settings in the system-provided services (e.g. by package upgrades) will be taken into account transparently.

6
  • If I see it correctly, systemctl edit actually creates some override file, is it true? I think, overriding WantedBy to am empty string would a nice solution, without the duplication of the configuration from the /lib. Is it possible?
    – peterh
    May 3, 2019 at 11:26
  • 1
    Yes, that’s correct. But the correct solution really is systemctl disable. (systemctl start can start a unit which doesn’t appear in systemctl --all.) May 3, 2019 at 11:27
  • 3
    And system list-unit-files has also the disabled entries... wonderful. Somewhere I've read this: "Systemd is a quite good OS, only it requires some improvement in its boot mechanism." ;-)
    – peterh
    May 3, 2019 at 11:37
  • I found that if you do the disable option on the MySQL service, systemd will stop MySQL in the middle of the night. Happened on all three of my servers that are a fresh install with only MySQL installed, at the exact same time: 6:38 AM UTC while I was fast asleep: 2022-02-04T06:38:37.905760Z 0 [System] [MY-013172] [Server] Received SHUTDOWN from user <via user signal>. Shutting down mysqld (Version: 8.0.27-0ubuntu0.20.04.1).
    – Tony Maro
    Feb 4 at 16:26
  • @TonyMaro that’s intriguing; I have a number of disabled services running, and they never stop unless I manually stop them. Feb 7 at 8:41
12

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!

I.

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.

II.

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: RefuseManualStart= and RefuseManualStop=:

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.

III.

A point to elaborate on, suggested by your idea of commenting the [Install] section in the service file:

[Install]
#WantedBy=multi-user.target

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):

[Install]
WantedBy=

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.
[Unit]
Before=systemd-growfs@-.service

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.

IV.

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 RefuseManualStart and 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 BindsTo=opt.mount and 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.

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.