---
title: Introducción a systemd
description: Convierte tus aplicaciones en servicios administrados y monitorizados sin necesidad de aplicaciones externas.
url: https://www.felixsanz.dev/es/articulos/introduccion-a-systemd
language: es
published: 2016-10-07
updated: 2016-10-07
tags:
  - linux
alternate-en: https://www.felixsanz.dev/articles/introduction-to-systemd
---

# Introducción a systemd

## Introducción

[systemd](https://www.freedesktop.org/wiki/Software/systemd) es una poderosa (aunque [controvertida](https://suckless.org/sucks/systemd/)) suite utilizada para **administrar y configurar sistemas Unix**. Uno de los usos más extendidos de systemd es el de **gestionar el sistema de inicio** (*init system*), para administrar fácilmente procesos y servicios. Aunque systemd es capaz de administrar muchas más cosas, en este artículo veremos como utilizar systemd para crear, arrancar, parar, reiniciar y, en general, mantener sanos y monitorizados nuestros servicios propios.

Vamos a utilizar de ejemplo una aplicación creada en Node.js. Si bien podríamos utilizar uno de los muchos gestores de procesos que se instalan a través de `npm`, como por ejemplo [pm2](https://github.com/Unitech/pm2), [forever](https://github.com/foreverjs/forever) o [supervisor](https://github.com/Supervisor/supervisor), el problema es que estas herramientas están unidas a Node.js y su futuro dependen del mantenimiento que reciban estas librerías. En el caso de systemd, es una suite muy sólida que viene de serie en muchas distribuciones Linux y saber cómo funciona nos brinda nuevas oportunidades.

Otro problema es que tras reiniciar la máquina necesitaríamos un servicio o tarea que ejecute de nuevo nuestro gestor de procesos basado en Node.js. Así pues, vamos a prescindir de este tipo de herramientas, aunque podríamos combinarlas y obtener lo mejor de ambos mundos.

Primero veremos como crear unidades, después como administrarlas, y por último como monitorizarlas.

## systemd

Systemd trabaja con 2 conceptos: ***units*** y ***targets***.

### Units

Las unidades son **componentes** (como por ejemplo servicios) que deberían funcionar como piezas independientes de software. En este caso, una unidad sería por ejemplo MySQL, Redis o Node.js.

Salvo que tu distribución Linux sea muy especial, guardarás tus unidades en la ruta `/etc/systemd/system` con un nombre tipo `app.service`. Para servicios de usuario también es posible almacenarlos en `/home/$USER/.config/systemd/user`.

Una unidad **se compone de secciones**, como por ejemplo `[Unit]`, `[Service]` o `[Install]`. Estas secciones **contienen directivas** y aquí veremos unas cuantas necesarias para nuestro cometido. Si quieres ver todas las secciones disponibles o sus directivas en detalle, consulta la documentación oficial.

En el caso de `[Unit]`, encontraremos **metadatos que definen la unidad**, como por ejemplo `Description` para añadir una descripción de texto, o `Requires`, `Wants`, `Before`, `After`, `BindsTo` o `Conflicts` que sirven para relacionar nuestra unidad con otras unidades.

Por ejemplo si nuestra unidad Node.js indica `Requires=redis.service`, se iniciará la unidad de Redis antes de que arranque la de Node.js.

La sección `[Install]` se encarga de interactuar con los *targets*, algo que veremos más adelante.

Por último, en `[Service]` indicaremos el funcionamiento de nuestra unidad. Unas cuantas directivas útiles son:

-   **Directivas de control de flujo**: `ExecStart`, `ExecStartPre`, `ExecStartPost`, `ExecReload`, `ExecStop` y `ExecStopPost`.
-   **Directivas de control de fallos**: `RestartSec` (tiempo a esperar para reiniciar la unidad tras un fallo), `Restart` (directiva para definir nuestra política de reinicio cuyo valor puede ser `always`, `on-success`, `on-failure`, `on-abnormal`, `on-abort` o `on-watchdog`) o `TimeoutSec` (directiva con la que especificamos cuanto tiempo esperar para que la unidad sea considerada como fallida).
-   **Otras directivas**: A modo de ejemplo, `Environment` se encargaría de pasar variables de entorno a nuestra aplicación (se pueden utilizar tantas como queramos), mientras que `User` y `Group` se encargarían de asignar permisos de ejecución. `PIDFile` también es útil cuando queramos asociar un fichero *pid* a nuestro servicio.

Puedes consultar la documentación sobre estas secciones así como sus directivas en [Unit/Install](https://www.freedesktop.org/software/systemd/man/systemd.unit.html) y [Service](https://www.freedesktop.org/software/systemd/man/systemd.service.html).

Vamos al lío. Nuestro servicio basado en Node.js lo definiremos en un fichero llamado `app.service` con el siguiente contenido:

```
[Unit]
Description=Node HTTP service

[Service]
Environment="MY_PORT=3000"
ExecStart=/usr/bin/node /srv/http/app/index.js
Restart=on-failure

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

Sencillo, ¿verdad?

Podríamos utilizar más directivas como `RestartSec` pero en principio vamos a dejar esas directivas con sus valores por defecto.

En cuanto las directivas de control de flujo (como `ExecStart`), hay una opción que nos ayudará muy a menudo. Cambiando `ExecStart=/usr/bin/node /srv/http/app/index.js` por `ExecStart=-/usr/bin/node /srv/http/app/index.js` (es decir, añadiendo un guión antes del comando) evitaremos que la unidad sea considerada como fallida en caso de que el comando devuelva un resultado considerado como erróneo, evitando así el reinicio, el reporte en los registros, etc. Esto es especialmente útil cuando queramos ejecutar comandos que sean opcionales y no nos importe si la salida es válida o errónea, puesto que el error será silenciado e ignorado (silenciado).

Si te preguntas por qué no hemos añadido `After=network.target`, es porque el *target* `multi-user` ya depende de la conexión de red. Más adelante veremos qué son los *targets* y como hacer para habilitar nuestro servicio en uno (en este caso `multi-user`) para que se encargue de arrancar nuestro proceso automáticamente tras iniciar/reiniciar el sistema.

#### Templates

A pesar de que no hemos utilizado plantillas para nuestra unidad si merece la pena hacer una mención a dicha funcionalidad. Como adivinarás, **su uso es perfecto para la clusterización de componentes**.

Para generar una plantilla llamaremos al fichero `app@.service` (añadir un símbolo arroba). Esto será un *placeholder* para que nuestras instancias adquieran el nombre de `app@1.service`, `app@2.service`, etc.

Después utilizaríamos el argumento generado a través de la variable `%i`. Podríamos generar una variable de entorno tipo `Environment=LISTEN_PORT=300%i`, y así en nuestra aplicación recibiríamos dicha variable para así ejecutar varias instancias de nuestra aplicación corriendo bajo diferentes puertos (300**1**, 300**2**, etc).

### Targets

Los *targets* sirven para **agrupar unidades**. Pueden compararse a *runlevels* en otros sistemas de inicio aunque a diferencia de estos, una unidad puede pertenecer a varios *targets* al mismo tiempo e incluso un *target* puede agrupar otros *targets*. Veamos algunos ejemplos para entenderlo mejor.

Un caso sencillo podría ser que tuvieramos un servicio que arranca con el sistema y necesita de interfaz gráfica para funcionar. En ese caso, esta unidad formaría parte del *target* llamado `graphical.target`.

Otro ejemplo sería un servicio que se encargase de reproducir música de alguna radio online y se iniciase al arrancar el sistema. Tiene sentido que esta unidad dependa de `sound.target` y `network.target`, ¿verdad?

En el caso de *targets* agrupando *targets*, el caso más claro es `multi-user`, un *target* que al ejecutarse indica a nuestro sistema que ya está preparado para aceptar inicios de sesión de usuarios del sistema. Este *target* depende directa e indirectamente de otros, como por ejemplo `systemd-networkd.target`, `swap.target` o `getty.target`, por lo que si tenemos disponible el *target* `multi-user`, será porque los otros se han iniciado correctamente.

Cuando instalemos una unidad en un *target* lo que en realidad se crea es un enlace simbólico (*symlink*). Estos enlaces se encuentran en la ruta `/etc/systemd/system/*.target.wants/` (por ejemplo `/etc/systemd/system/multi-user.target.wants/` en el caso de `multi-user.target`). En el caso de utilizar la ruta de usuario sería `/home/$USER/.config/systemd/user/*.target.wants/`.

## systemctl

Ahora mismo os estaréis preguntando como hacer para arrancar una unidad, instalarla, etc. Bienvenidos a `systemctl`.

`systemctl` es un comando para **administrar y controlar el funcionamiento de systemd**. En este apartado veremos unos cuantos usos generales pero útiles.

> [!NOTE]
> **Ser o no ser**
> `systemctl` corre bajo el usuario **root** así que necesita permisos de administrador, a no ser que utilicemos el parámetro `--user`, algo útil para correr servicios bajo nuestro usuario.

### Arrancar y parar

Para arrancar una unidad:

```
systemctl start app.service
```

Para parar una unidad:

```
systemctl stop app.service
```

### Reiniciar y recargar

Para reiniciar una unidad:

```
systemctl restart app.service
```

Si nuestra aplicación es capaz de recargar su configuración sin reiniciar podremos utilizar:

```
systemctl reload app.service
```

Y si no estamos seguros de ello:

```
systemctl reload-or-restart app.service
```

### Habilitar y deshabilitar

Para que nuestra unidad arranque al iniciar el sistema (o más bien, cuando se inicie el *target* asociado a nuestra unidad), debemos habilitar la unidad mediante el siguiente comando:

```
systemctl enable app.service
```

> [!WARNING]
> **¡No funciona!**
> Habilitar una unidad **no hace que arranque en ese preciso momento**. Para eso debemos utilizar `systemctl start app.service` o simplemente `systemctl enable --now app.service`.

Para deshabilitarla:

```
systemctl disable app.service
```

Recordemos que esto lo que hace es crear o borrar un enlace simbólico.

### Estado

Para comprobar el estado de nuestra aplicación, utilizaremos:

```
systemctl status app.service
```

```
● app.service - Node HTTP service
   Loaded: loaded (/etc/systemd/system/app.service; disabled; vendor preset: disabled)
   Active: active (running) since jue 2016-10-06 18:58:00 CEST; 5s ago
 Main PID: 30453 (node)
    Tasks: 6 (limit: 4915)
   Memory: 8.4M
      CPU: 77ms
   CGroup: /system.slice/app.service
           └─30453 /usr/bin/node /srv/http/app/index.js

oct 06 18:58:00 earth systemd[1]: Started Node HTTP service.
oct 06 18:58:00 earth node[30453]: Server running at http://127.0.0.1:3000/
```

También podemos comprobar el estado de nuestra unidad de una manera más directa mediante varios comandos:

```
systemctl is-active app.service
systemctl is-enabled app.service
systemctl is-failed app.service
```

### Enmascarar y desenmascarar

Si necesitamos enmascarar nuestra unidad para que no arranque de ninguna manera (ni automática ni manualmente), podemos utilizar:

```
systemctl mask app.service
```

Esto hará que nuestra unidad apunte a `/dev/null` y no se inicie.

Y para desenmascarar:

```
systemctl unmask app.service
```

### Ver, editar y borrar unidades

Estas operaciones pueden realizarse por separado o mediante `systemctl`. Para ver el contenido de una unidad, `systemctl` nos provee del siguiente comando:

```
systemctl cat app.service
```

Y si queremos acceder a una información de más bajo nivel lo hacemos mediante:

```
systemctl show app.service
```

Para editar una unidad contamos con `edit`, aunque no funciona tal como esperarías. Cuando editamos una unidad en realidad estamos creando un ***drop-in***. Se crea una carpeta asociada a cada unidad llamada `/etc/systemd/system/app.service.d/` (o `/home/$USER/.config/systemd/user/app.service.d/`) y dentro se crea un fichero que se encarga de proveer cambios a la unidad. En este fichero podemos reemplazar directivas a nuestro gusto, añadir nuevas, o devolver directivas a su estado inicial.

```
systemctl edit app.service
```

Si queremos editar la unidad sin utilizar *drop-ins*, lo hacemos de la siguiente manera:

```
systemctl edit --full app.service
```

Podemos borrar tanto *drop-ins* como la unidad entera mediante el comando `rm`.

Si hemos realizado modificaciones sin utilizar `systemctl edit` o hemos borrado algo mediante `rm`, informaremos a `systemctl` de nuestros cambios ejecutando este comando:

```
systemctl daemon-reload
```

### Instancias

Si utilizamos plantillas podemos realizar operaciones a múltiples instancias de la siguiente manera:

```
systemctl start app@1.service
systemctl start app@2.service
```

También podemos realizar operaciones en varias unidades a la vez mediante la siguiente sintaxis (esto depende de nuestro intérprete de comandos):

```
systemctl start app@{1,2,3,4,5}.service
systemctl start app@{1..5}.service
```

### Targets

Por supuesto `systemctl` nos provee de comandos para realizar operaciones sobre *targets*, como por ejemplo cambiar el *target* por defecto del sistema, poner la máquina en un *target* específico, etc. En nuestro caso, solo vamos a ver los comandos para consultar la lista de *targets* que tenemos disponibles en nuestra máquina.

Versión resumida:

```
systemctl list-unit-files --type=target
```

Versión detallada:

```
systemctl list-units --type=target
```

Mediante `--type` podemos filtrar unidades de otros tipos.

### Dependencias

Para consultar la lista de dependencias tanto de nuestras unidades como de los *targets* utilizaremos:

```
systemctl list-dependencies app.service
systemctl list-dependencies multi-user.target
```

## journalctl

Ya sabemos como crear, iniciar, parar y en general, administrar nuestras aplicaciones mediante systemd, pero esta completa suite aún tiene algo poderoso que ofrecernos: `journalctl`.

`journalctl` es un comando para **visualizar registros** (*logs*) de nuestras unidades (o del sistema en general). Vamos a ver unos cuantos comandos útiles para facilitarnos el día a día.

Si simplemente ejecutamos `journalctl` veremos registros de todo nuestro sistema desde el principio de los tiempos. El primer argumento que nos vendrá bien es `--utc`, que como habrás podido adivinar nos mostrará los registros con fecha y hora acorde a [UTC](https://es.wikipedia.org/wiki/Tiempo_universal_coordinado).

> [!NOTE]
> **Root o no root**
> Recuerda que `journalctl` también corre bajo el usuario **root**. Si vas a monitorizar servicios del sistema utiliza `sudo`, o el parámetro `--user` si solo vas a consultar registros de un servicio que corre bajo tu usuario.

### Filtrar por inicio de máquina

`journalctl` es capaz de segmentar nuestros registros por inicios del sistema. Para ver los registros desde el último reinicio de la máquina utilizaremos:

```
journalctl -b
```

Si queremos ver los registros del penúltimo reinicio, utilizaremos:

```
journalctl -b -1
```

Luego iría `-2`, `-3`... y así sucesivamente. Si queremos consultar cuantos reinicios han habido así como su posición, identificador y rango de fechas, utilizaremos:

```
journalctl --list-boots
```

```
-2 ae4450adc26e47c69f943bf54c1ec488 dom 2016-08-28 08:43:36 CEST—sáb 2016-09-10 23:29:04 CEST
-1 16e0c81dff134320920dc07822ddc4b3 sáb 2016-09-10 23:29:38 CEST—mié 2016-09-21 18:54:58 CEST
 0 31c4459b64c1449184700bd6cccb09aa mié 2016-09-21 19:17:47 CEST—jue 2016-10-06 19:55:08 CEST
```

### Filtrar por fecha

Para filtrar por fecha utilizaremos los argumentos `since` y `until`, especificando el valor en formato `YYYY-MM-DD HH:MM:SS`. Ejemplo:

```
journalctl --since "2016-10-01" --until "2016-10-07 01:00"
```

> [!NOTE]
> **Omitir datos**
> Podemos omitir fragmentos como los segundos o la hora entera y estos adoptarán el valor `00`.

También reconoce otros formatos relativos como `yesterday`, `today` o `now`:

```
journalctl --since yesterday --until "2 hours ago"
```

### Filtrar por unidad

Si solo queremos ver los registros de una unidad en concreto utilizaremos el argumento `u` (de *unit*):

```
journalctl -u app.service
```

### Otros argumentos

Merece la pena destacar otros argumentos como el formato de salida (*output*) que acepta valores como `json` o `json-pretty` entre otros. Ejemplo:

```
journalctl -o json
journalctl -o json-pretty
```

El argumento `n` nos mostrará los últimos **N** registros, siendo por defecto 10 si no le indicamos un número.

```
journalctl -n
journalctl -n 20
journalctl -n 100
```

Y por último pero no menos importante, el argumento `f` seguirá las actualizaciones del registro en tiempo real, de igual manera que haríamos con `tail -f`:

```
journalctl -f
```

## Conclusión

Hemos visto como funciona systemd a grandes rasgos: como crear unidades, como administrarlas mediante `systemctl`, y como `journalctl` se convierte en nuestro poderoso aliado para monitorizar y depurar el estado de nuestros procesos y servicios.

Por supuesto el potencial de toda esta suite daría como para escribir una biblia. Aquí hemos visto, de manera rápida, el funcionamiento básico para poder empezar a utilizar systemd para nuestros servicios, evitando utilizar herramientas específicas que solo nos servirían para casos específicos.
