Home » Why do most systemd examples contain WantedBy=multi-user.target?

Why do most systemd examples contain WantedBy=multi-user.target?


1.) multi-user.target is basically the closest equivalent of classic SysVinit runlevel 3 that systemd has. When a systemd system boots up, systemd is trying to make the system state match the state specified by default.target – which is usually an alias for either graphical.target or multi-user.target.

multi-user.target normally defines a system state where all network services are started up and the system will accept logins, but a local GUI is not started. This is the typical default system state for server systems, which might be rack-mounted headless systems in a remote server room.

graphical.target is another possible alias for default.target. Normally it’s defined as a superset of the multi-user.target: it includes everything the multi-user.target does, plus the activation of a local GUI login. So kind of like runlevel 5 in classic SysVinit.

The line WantedBy=multi-user.target in a service is essentially the same as specifying “this service should start in runlevels 3, 4 and 5” in SysVinit systems: it tells systemd that this service should be started as part of normal system start-up, whether or not a local GUI is active.

However, WantedBy is separate from the enabled/disabled state: so in another sense, it’s sort of a “preset”: it determines under what conditions the automatic start may happen, but only when the service is enabled in the first place.

2.) if you omit the WantedBy=multi-user.target line and no other enabled service includes a Requires=your.service or Wants=your.service in its service definition, your service will not be started automatically.

systemd works on dependencies, and at boot time, if nothing Requires or Wants your service, it won’t be started even if the service is enabled.

Sure, you could edit your default.target to add or delete Requires or Wants lines for any services you want started at boot time – but so that you can just drop a new service file into the system and have it work by default (which makes things very easy for software package managers), systemd has the WantedBy and RequiredBy keywords which can be used to insert Wants and Requires-type dependencies (respectively) from “the other end”.

3.) You should omit the line if you don’t want the service to be ever started automatically at boot time, or this service is a part of a chain of dependencies you’ve defined explicitly.

For example, you might be refactoring server application A and for some reason or another decide to split some optional functionality off it into a separate service B, to allow the user the choice of not installing it if it isn’t needed. You could then make service B a separate service-B.rpm, and define B.service with WantedBy=A.service to make systemd start up service B automatically whenever service A is started – but only when service-B.rpm is actually installed.

Note that a Wants or WantedBy only says that the system should startup one service whenever another service or target is also started, but it specifies nothing at all about the startup/shutdown order. If you need service B to be already running when service A starts up, you’d need to add Before=A.service in the B.service file to explicitly specify the start-up order dependency.

4.) Anytime you do want the service to have the capability of being started automatically at boot time, and there are no other dependencies already defined.

If you remove WantedBy=multi-user.target, then systemctl enable your-example-here will (noisily) fail to do anything.


If you install pure systemd from the source, the “default target” that it boots to is graphical.target.

Starting graphical.target starts multi-user.target, plus whatever unit(s) are required to provide a graphical user interface. This extra complexity was arranged in an attempt to emulate legacy “runlevels”.

You really should ignore / gloss over the “runlevel” emulation; it does not work correctly anyway. Sorry! I guess the reason for emphasizing “graphical” v.s. “multi-user” historically is that graphics software is 1) not as robust and mature as the rest of the system and 2) requires a lot of resources.

Typically only a few units are specific to graphical.target. There is a single service for the GUI itself like gdm.target. There are a few support services that are mostly used by the GUI here as well.

Edit: Googling suggests that if you don’t have a GUI installed, but the “default target” has been left as graphical.target, then systemd might log a warning. “Cannot add dependency job for unit display-manager.service, ignoring: Unit display-manager.service failed to load: No such file or directory.” We want to avoid littering our logs with unnecessary warnings. So if you did not install a GUI, it is good to use systemctl set-default multi-user. Although the install system for your OS might have taken care of this for you already. Other than that, I am strongly in favour of apathy in this matter :-).


Some services and other types of units are “involved in early boot”. They are defined to start Before=sysinit.target – either directly or indirectly. Most services are only started After=sysinit.target – this is automatically the case, unless the service sets DefaultDependencies=no.


Most example services do not fall under either of the above categories, therefore we attach them to multi-user.target. This includes most network services (e.g. a web server), which are the archetypal system service.

dynamically activated services

Another possibility you might see, is a service unit that is not started automatically at boot. So it would not need WantedBy=multi-user.target. The service can instead be triggered or “activated” by something else.

One example of this is a dbus activated service. Dbus can be configured to start your service on-demand, when a dbus call is made to the service.

For network services, you can use socket-activated services. This might be easier to find details about, because all the configuration is in systemd units. For example sshd.socket or ssh.socket is typically available to activate ssh@.service or sshd@.service. Although, I think it is more common to start the sshd service at boot time.

As always, the above simplifies and omits details which did not seem to be required.

The multi-user.target is a semantic name, that is, it is associated with a meaning. Allow me to demonstrate the concept with a counterexample. I will make the argument that the decision to use multi-user.target depends on the context.

If you were to create a systemd user unit configuration rather than a system unit configuration, you might naively follow the conventions of the systemd system units and use multi-user.target1.

Explanation with Example

Consider the file /home/jonathan/.config/systemd/user/coolstuff.service

This user will only by used with respect to user jonathan, therefore it hardly makes sense to call my target “multi-user.target”.

Description=Does some cool stuff.

ExecStart=/bin/bash -c '/usr/bin/echo "Cool stuff" >> %h/coolstuff.txt'


Note how I opted to use WantedBy=user.target. This is because the scope of this context is my user account, therefore I opted to use my own semantic conventions. In other words, multiple users will not be affected by this unit. On the system level however, it makes more sense to follow standardized conventions because of the parallel startup sequence. During startup, many procedures converge on multiple targets. The multi-user.target is semantically associated with the idea of “read for user engagement / interaction with the system” (without a windowing system as telcoM mentioned), therefore it is a safe bet for most system unit files.


  1. Although it would not make sense from within this context, it would still work.

Related Solutions

Joining bash arguments into single string with spaces

[*] I believe that this does what you want. It will put all the arguments in one string, separated by spaces, with single quotes around all: str="'$*'" $* produces all the scripts arguments separated by the first character of $IFS which, by default, is a space....

AddTransient, AddScoped and AddSingleton Services Differences

TL;DR Transient objects are always different; a new instance is provided to every controller and every service. Scoped objects are the same within a request, but different across different requests. Singleton objects are the same for every object and every...

How to download package not install it with apt-get command?

Use --download-only: sudo apt-get install --download-only pppoe This will download pppoe and any dependencies you need, and place them in /var/cache/apt/archives. That way a subsequent apt-get install pppoe will be able to complete without any extra downloads....

What defines the maximum size for a command single argument?

Answers Definitely not a bug. The parameter which defines the maximum size for one argument is MAX_ARG_STRLEN. There is no documentation for this parameter other than the comments in binfmts.h: /* * These are the maximum length and maximum number of strings...

Bulk rename, change prefix

I'd say the simplest it to just use the rename command which is common on many Linux distributions. There are two common versions of this command so check its man page to find which one you have: ## rename from Perl (common in Debian systems -- Ubuntu, Mint,...

Output from ls has newlines but displays on a single line. Why?

When you pipe the output, ls acts differently. This fact is hidden away in the info documentation: If standard output is a terminal, the output is in columns (sorted vertically) and control characters are output as question marks; otherwise, the output is...

mv: Move file only if destination does not exist

mv -vn file1 file2. This command will do what you want. You can skip -v if you want. -v makes it verbose - mv will tell you that it moved file if it moves it(useful, since there is possibility that file will not be moved) -n moves only if file2 does not exist....

Is it possible to store and query JSON in SQLite?

SQLite 3.9 introduced a new extension (JSON1) that allows you to easily work with JSON data . Also, it introduced support for indexes on expressions, which (in my understanding) should allow you to define indexes on your JSON data as well. PostgreSQL has some...

Combining tail && journalctl

You could use: journalctl -u service-name -f -f, --follow Show only the most recent journal entries, and continuously print new entries as they are appended to the journal. Here I've added "service-name" to distinguish this answer from others; you substitute...

how can shellshock be exploited over SSH?

One example where this can be exploited is on servers with an authorized_keys forced command. When adding an entry to ~/.ssh/authorized_keys, you can prefix the line with command="foo" to force foo to be run any time that ssh public key is used. With this...

Why doesn’t the tilde (~) expand inside double quotes?

The reason, because inside double quotes, tilde ~ has no special meaning, it's treated as literal. POSIX defines Double-Quotes as: Enclosing characters in double-quotes ( "" ) shall preserve the literal value of all characters within the double-quotes, with the...

What is GNU Info for?

GNU Info was designed to offer documentation that was comprehensive, hyperlinked, and possible to output to multiple formats. Man pages were available, and they were great at providing printed output. However, they were designed such that each man page had a...

Set systemd service to execute after fstab mount

a CIFS network location is mounted via /etc/fstab to /mnt/ on boot-up. No, it is not. Get this right, and the rest falls into place naturally. The mount is handled by a (generated) systemd mount unit that will be named something like mnt-wibble.mount. You can...

Merge two video clips into one, placing them next to each other

To be honest, using the accepted answer resulted in a lot of dropped frames for me. However, using the hstack filter_complex produced perfectly fluid output: ffmpeg -i left.mp4 -i right.mp4 -filter_complex hstack output.mp4 ffmpeg -i input1.mp4 -i input2.mp4...

How portable are /dev/stdin, /dev/stdout and /dev/stderr?

It's been available on Linux back into its prehistory. It is not POSIX, although many actual shells (including AT&T ksh and bash) will simulate it if it's not present in the OS; note that this simulation only works at the shell level (i.e. redirection or...

How can I increase the number of inodes in an ext4 filesystem?

It seems that you have a lot more files than normal expectation. I don't know whether there is a solution to change the inode table size dynamically. I'm afraid that you need to back-up your data, and create new filesystem, and restore your data. To create new...

Why doesn’t cp have a progress bar like wget?

The tradition in unix tools is to display messages only if something goes wrong. I think this is both for design and practical reasons. The design is intended to make it obvious when something goes wrong: you get an error message, and it's not drowned in...