---
categories: system sysadmin
title: Howto systemd
...

* Manpages : <https://www.freedesktop.org/software/systemd/man/index.html>

[systemd](https://www.freedesktop.org/wiki/Software/systemd/) est un gestionnaire de services (le fameux démon avec le PID 1) en alternative au System V. Il est installé par défaut depuis Debian 8. Son utilisation a suscitée des débats au sein de l'équipe, mais nous l'utilisons sur tous nos serveurs en Debian 8.


## Utilisation de base

### Statut systemd

Afficher le statut :

~~~
# systemctl status
~~~

Lister les unités qui tournent :

~~~
# systemctl
# systemctl list-units
~~~

Lister les unités en échecs :

~~~
# systemctl --failed
~~~

Lister les unités installées : 

~~~
# systemctl list-unit-files
~~~

> *Note* : Les unités sont par défaut dans `/usr/lib/systemd/system/` et `/etc/systemd/system/` (gestion manuelle)


### Gestion des unités

<https://www.freedesktop.org/software/systemd/man/systemd.unit.html>

Démarrer/arrêter/redémarrer une unité :

~~~
# systemctl start <unité>
# systemctl stop <unité>
# systemctl restart <unité>
~~~

Recharger la configuration d'une unité : 

~~~
# systemctl reload <unité>
~~~

Afficher le statut d'une unité :

~~~
# systemctl status <unité>
# systemctl status <unité> -l
~~~

Savoir si une unité est activée ou non :

~~~
# systemctl is-enabled <unité>
~~~

> *Note* : Attention, pour les pseudo-unités (qui sont en fait des scripts dans `/etc/init.d/`), `is-enabled` ne fonctionne pas.

Activer/désactiver une unité au boot :

~~~
# systemctl enable <unité>
# systemctl disable <unité>
~~~

Masquer/démasquer une unité (_mask_ rend impossible tout stop/start/…) :

~~~
# systemctl mask <unité>
# systemctl unmask <unité>
~~~

Afficher la man page correspondante au service de l'unité : 

~~~
# systemctl help <unité>
~~~

Recharger systemd pour prendre en compte les unités modifiées : 

~~~
# systemctl daemon-reload
~~~


### Redémarrer ou éteindre un serveur

Avec systemd, les commandes halt/poweroff/shutdown/reboot n'existent plus !
Ce sont des désormais des liens symboliques vers **systemctl**.  
On conseille donc d'utiliser directement les commandes « natives ».

Redémarrer le serveur : 

~~~
# systemctl reboot
~~~

Éteindre et couper l'alimentation du serveur : 

~~~
# systemctl poweroff
~~~

> *Note* : l'option `--force` permet de killer tous les process sans attendre leur extinction propre (à éviter bien sûr)


## Rédaction des unités 

<https://www.freedesktop.org/software/systemd/man/systemd.service.html>

### Modifier une unité 

~~~
# cp -a /lib/systemd/system/<service>.service /etc/systemd/system/
# vim /etc/systemd/system/<service>.service
# systemctl daemon-reload
~~~

Exemple avec ssh.service sous Debian 8 :

~~~
[Unit]
Description=OpenBSD Secure Shell server
After=network.target auditd.service
ConditionPathExists=!/etc/ssh/sshd_not_to_be_run

[Service]
EnvironmentFile=-/etc/default/ssh
ExecStart=/usr/sbin/sshd -D $SSHD_OPTS
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
Restart=on-failure

[Install]
WantedBy=multi-user.target
Alias=sshd.service
~~~

> **Note** : Chaque fois qu'une unité est modifiée, il est nécessaire de recharger systemd avec `systemctl daemon-reload`.


### Modifier partiellement une unité

Au lieu de tout ré-écrire, on peut surcharger une partie de l'unité en créant un fichier dans `/etc/systemd/system/NOM_UNITÉ.service.d/` qui précisera les modifications à faire. Exemple avec l'unité de _varnish_ on crée le fichier `/etc/systemd/system/varnish.service.d/override.conf` :

~~~
[Service]
ExecStart=
ExecStart=/usr/sbin/varnishd -a 0.0.0.0:80 -T localhost:6082 -f /etc/varnish/default.vcl -S /etc/varnish/secret -s malloc,16G -p thread_pools=8 -p thread_pool_add_delay=2 -p thread_pool_min=500 -p thread_pool_max=5000
ExecReload=
ExecReload=/etc/varnish/reload-vcl.sh
~~~

A noter que le "ExecReload" et le "ExecReload" vides ne sont pas des erreurs mais permettent d'effacer les valeurs présentes dans l'unité par défaut avant de les réassigner.

On peut directement utiliser la commande suivante pour surcharger une unité :

~~~
# systemctl edit varnish
~~~

On pourra ensuite lister l'unité complète vue par _systemd_ :

~~~
# systemctl cat varnish
~~~


## Utiliser les templates d'unités

On peut créer/utiliser des templates d'unités. Cela permet de gérer à la volée des instances de services.

Par exemple pour _redis_ via `/etc/systemd/system/redis@.service`.

~~~
[Unit]
Description=Advanced key-value store instance %i
After=network.target

[Service]
Type=forking
ExecStart=/usr/bin/redis-server /etc/redis/%i.conf
ExecStartPost=/bin/chgrp %i /var/run/redis/%i.sock
ExecStop=/usr/bin/redis-cli shutdown
Restart=always
User=redis
Group=%i

[Install]
WantedBy=multi-user.target
~~~

`%i` correspond au nom d'instance après le `@`.
On pourra ensuite démarrer l'instance foo et bar avec :

~~~
# systemctl start redis@foo redis@bar
~~~


## systemd par utilisateur

systemd fonctionne avec le PID 1. Mais il est aussi possible de lancer des instances par utilisateur avec le paramètre `--user`. C'est très pratique pour donner les droits à un utilisateur de gérer lui-même ses services (instances _Tomcat_ par exemple).  
Il faut avoir le paquet `libpam-systemd` installé : une session systemd est lancée quand on est connecté à la machine :

~~~
$ systemctl --user status
● bar
    State: running
     Jobs: 0 queued
   Failed: 0 units
    Since: ven. 2016-12-23 21:15:33 CET; 1 weeks 6 days ago
   CGroup: /user.slice/user-1001.slice/user@1001.service
           ├─1758 /lib/systemd/systemd --user
           └─1760 (sd-pam)  
~~~

Attention, si l'on veut une session persistante pour un utilisateur, il faut l'activer avec `loginctl` (si l'on ne fait pas cela, les services lancés via `systemctl --user` seront coupés à la déconnexion). Par exemple pour l'utilisateur _foo_ :

~~~
# loginctl enable-linger foo
~~~

Attention, si l'on renomme l'utilisateur foo ou que l'on change son UID, il faut supprimer son "linger" :

~~~
# rm /var/lib/systemd/linger/foo
~~~

L'utilisateur peut créer son unité systemd dans son */home* dans le dossier suivant :

~~~
~/.config/systemd/user/
~~~

A créer si les dossier n'existe pas.

Exemple d'unité utilisateur pour _Tomcat_ :

`/etc/systemd/user/tomcat.service`

~~~
[Unit]
Description=Tomcat %u.
After=network.target

[Service]
WorkingDirectory=/home/%u
Environment="CATALINA_BASE=/home/%u/tomcat"
EnvironmentFile=/home/%u/tomcat/conf/env
UMask=0002
ExecStart=/usr/share/tomcat7/bin/startup.sh
ExecStop=/usr/share/tomcat7/bin/shutdown.sh
ExecStopPost=/bin/sh -c date | /usr/bin/mail -s "%H/%u : Shutdown instance" foo@bar.com
Type=forking

[Install]
WantedBy=default.target
~~~

L'utilisateur pourra ainsi gérer lui-même le service :

~~~
$ systemctl --user enable tomcat
$ systemctl --user start tomcat
$ systemctl --user status -l tomcat
$ systemctl --user restart tomcat
$ systemctl --user stop tomcat
~~~

*Note* : pour pouvoir gérer `systemctl --user` via `sudo -iu foo`, on conseille de mettre dans `/etc/profile.d/systemd.sh` :

~~~
export XDG_RUNTIME_DIR="/run/user/$UID"
~~~


## Utilisation avancée
	
Afficher plein d'informations sur l'unité :

~~~
# systemctl show <service>
~~~

_Debug_ au démarrage accessible sur _tty9_ :

~~~
# systemctl enable debug-shell
~~~

Débug d'une unité :

~~~
# systemctl stop unit
# SYSTEMD_LOG_LEVEL=debug /lib/systemd/unit
~~~

Analyser ce qui prend du temps au démarrage :

~~~	
# systemd-analyze 
Startup finished in 5.019s (firmware) + 6.128s (loader) + 5min 35.864s (kernel) + 20min 3.148s (userspace) = 25min 50.160s
# systemd-analyze blame | head -3
2.029s systemd-udev-settle.service
1.400s docker.service
1.215s uml-utilities.service
~~~


## FAQ

### systemd et Debian 8

Sous Debian 8, l'adoption de systemd est récente et de nombreuses services ont des unités mal écrites. Notamment elles ne gèrent souvent pas les options dans `/etc/default/` alors qu'un fichier est créé par défaut.
On conseille alors de corriger l'unité. En général, les corrections sont intégrées en Debian 9.


Exemple avec [Bind](HowtoBind) qui ne gère par les options dans `/etc/default/bind9` en Debian 8.
Pour corriger il faut copier l'unité :

~~~
# cp -a /lib/systemd/system/bind9.service /etc/systemd/system/
~~~

et ajuster la section _[Service]_ :

~~~
EnvironmentFile=-/etc/default/bind9
ExecStart=/usr/sbin/named -f $OPTIONS
~~~

puis :

~~~
# systemctl daemon-reload
~~~

### strace d'un process

Avant _systemd_, une astuce efficace pour stracer un démon multi-process était de faire `strace -ff /etc/init.d/<démon> start` …mais ça n'est plus possible avec systemd : il faut désormais détecter le PID du processus père du démon pour faire un `strace -ff -p<PID>`.

Exemple pour Apache, on récupère le PID, on lance le _strace_ puis on fait un _graceful_ :

~~~
# pstree -pan | grep -v grep | grep apache | head -1
# strace -ff -s65535 -p<PID>
# apache2ctl graceful
~~~

### networking

Sous Debian 8, le démarrage du système se bloque complètement (dead lock) si l'initialisation de process dépendant du réseau (comme NFS, NTP, firewalling…) se fait avant que le réseau soit démarré.
Notamment quand ils sont démarrés via des hooks dans `/etc/network/if-*/`… Cela n'était pas bloquant avec SysV, cf <https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=754218#30>

Contournement « bourrin » : ajouter `exit 0` dans `/etc/init.d/networking` et voir le souci via `systemctl status networking -l` ; modifier `JobTimeoutUSer` => `systemctl show networking -p JobTimeoutUSer`

### Failed to get D-Bus connection: Unknown error -1

Si en utilisant `systemctl` vous obtenez une erreur "Failed to get D-Bus connection: Unknown error -1" vérifiez si vous êtes bien passé à systemd et n'êtes resté à sysvinit !

### Failed to get D-Bus connection: No such file or directory

Si en tant qu'utilisateur, on obtient l'erreur :

~~~
$ systemctl --user status
Failed to get D-Bus connection: No such file or directory
~~~

S'assurer que l'on a bien une variable d'environnement du type :

~~~
XDG_RUNTIME_DIR=/run/user/1042
~~~

Avec un _strace_ on devrait voir quelque chose comme :

~~~
$ strace systemctl --user status
connect(3, {sa_family=AF_LOCAL, sun_path="/run/user/1042/systemd/private"}, 32) = -1 ECONNREFUSED (Connection refused)
connect(3, {sa_family=AF_LOCAL, sun_path="/run/user/1042/bus"}, 20) = -1 ENOENT (No such file or directory)
~~~

C'est surtout le premier élément qui est intéressant. _systemd_ doit maintenir des sockets Unix fonctionnelles quand un utilisateur est logué :

~~~
$ lsof | grep /run/user/1042
systemd     844       foo   11u     unix 0xffff88082dbb9480        0t0   69531078 /run/user/1042/systemd/notify
systemd     844       foo   12u     unix 0xffff88082dbb9c00        0t0   69531080 /run/user/1042/systemd/private
~~~

Pour le forcer à les récréer on pourra tester dans l'ordre :

* la commande `loginctl enable-linger foo` pour les rendre persistantes et voir l'effet (quitte à désactiver après)
* `mv /var/lib/systemd/linger/foo /tmp` puis `loginctl enable-linger foo`
* `mv /run/user/1042` puis `loginctl enable-linger foo`
* redémarrer dbus : `systemctl restart dbus`
* redémarrer systemd-logind : `systemctl restart systemd-logind`
* stopper tous les process de l'utilisateur (voir avec lsof) puis se loguer "proprement" avec ssh ou login (et non pas sudo ou su)

Cas d'une installation non standard, tel un container, vérifiez que vous avez bien accès à `/dev`. On peut le vérifier avec dbus-launch.

~~~
foo@bar:~$ dbus-launch
Failed to open /dev/null: Permission denied
~~~

Parfois avec OpenVZ, les containers sont démarrés avec `/dev` en 700.

### systemd-fsck : Dependency failed

Vous obtenez _systemd-fsck : Dependency failed for File System Check on /dev/disk/foo_ ?

Par défaut, `systemd-fsck` attend 1m30 l'accès à chaque device disque. Pour diverses raisons (accès à un SAN externe, etc.) cela peut échouer, vous aurez alors :

~~~
systemd[1]: Job dev-disk-by\x2dlabel-foo.device/start timed out.
systemd[1]: Timed out waiting for device dev-disk-by\x2dlabel-foo.device.
systemd[1]: Dependency failed for /backup.
systemd[1]: Dependency failed for Local File Systems.
systemd[1]: Triggering OnFailure= dependencies of local-fs.target.
systemd[1]: Dependency failed for File System Check on /dev/disk/by-label/foo.
~~~	

Vous pouvez alors ajouter différentes options via le fstab, du type :

~~~
LABEL=foo    /foo ext4    defaults,nofail,x-systemd.device-timeout=120 0 0
~~~	

Notamment `nofail` pour éviter que cela fasse échouer votre séquence de boot !

### systemd VS systemD VS SystemD

**systemd** ne prend aucune majuscule. Ce n'est pas SystemD ou systemD.

### Migrer de sysvinit à systemd

Cette action peut être nécessaire lorsque l'on migre de Debian Wheezy à Jessie.  

~~~
# apt install systemd-sysv
The following packages will be REMOVED:
sysvinit-core
# reboot
~~~

et vérifier que GRUB ne se lance pas avec l'option `init=/lib/sysvinit/init`.

### Et les scripts dans /etc/init.d/ ?

Avec systemd, pourquoi reste-t-il des scripts dans `/etc/init.d/` ?

systemd prend en compte les scripts dans `/etc/init.d/` :

* les scripts « classiques » sont pris en compte si il n'existe pas d'unité systemd avec le même nom (sa description commence alors par **LSB:**)
* certains scripts sont juste là par compatibilité afin de pouvoir faire `/etc/init.d/foo start/stop/restart/status` mais c'est en fait l'unité systemd qui est prise en compte

## Exemples d'unités

### Process Node.js géré via forever

*forever*  est lui-même un gestionnaire de process, mais dans certains cas c'est pertinent d'empiler les 2 gestionnaires. Voici un exemple fonctionnel (qu'on pourrait vouloir transformer en unité "user", d'ailleurs) :

~~~
# /etc/systemd/system/foo_chat.service
[Unit]
Description=Node.js process for Foo chat
After=network.target

[Service]
ExecStart=/usr/local/bin/forever start --pidFile /home/foo/www/foo.pid -o /home/foo/www/log/out.log -e /home/foo/www/log/err.log /home/foo/www/bin/www
ExecStop=/usr/local/bin/forever stop /home/foo/www/bin/www
PIDFile=/home/foo/www/foo.pid 
Type=forking
LimitNOFILE=4096
User=nod
Restart=always

[Install]
WantedBy=default.target
~~~