Cómo crear un servicio en systemd en Linux
Aprenda a configurar un archivo .service en /etc/systemd/system/ para ejecutar programas, gestionar reinicios automáticos y usar systemd-timer.
Qué es un servicio de systemd y por qué es necesario
Un servicio de systemd es un archivo de texto pequeño que indica al servidor cómo ejecutar un programa: iniciarlo al arrancar, reiniciarlo si falla y enviar su salida al log del sistema. Esa es su única función. Un programa iniciado manualmente en una sesión SSH se detiene al cerrar la sesión o reiniciar el servidor. Un programa configurado como servicio de systemd sigue ejecutándose porque el servidor lo gestiona en lugar de su shell.
systemd es el sistema init en Ubuntu, Debian, Fedora y la mayoría de los servidores Linux modernos. Es el primer proceso en iniciarse y supervisa todos los demás procesos. Al crear un archivo de servicio, usted entrega su programa a ese supervisor. Esta guía muestra la unidad mínima funcional, las tres secciones que tiene cada unidad, cómo activarla y leer sus logs, cómo programar su ejecución con un timer y cómo restringir sus permisos para que se ejecute con el mínimo privilegio posible.
El servicio más simple que funciona
Un archivo de servicio se ubica en /etc/systemd/system/, tiene la extensión .service y solo requiere unas pocas líneas. Cree uno para un programa en /usr/local/bin/myapp:
sudo nano /etc/systemd/system/myapp.service[Unit]
Description=My application
[Service]
ExecStart=/usr/local/bin/myapp
[Install]
WantedBy=multi-user.targetEsa es una unidad completa y funcional. ExecStart es el comando de ejecución. WantedBy=multi-user.target indica iniciar el servicio cuando el servidor alcance el modo multiusuario normal, lo que permite su ejecución durante el arranque. El resto son ajustes de optimización.
Las tres secciones y su función
Cada archivo de unidad se divide en secciones entre corchetes. Un servicio utiliza tres.
[Unit] describe el servicio y sus relaciones. Las dos líneas que más utilizará son:
[Unit]
Description=My application
After=network-online.target
Wants=network-online.targetDescription es la etiqueta legible para humanos que aparece en systemctl status. After=network-online.target indica a systemd que no inicie su programa hasta que la red esté activa; esto es relevante para cualquier proceso que asigne un puerto o realice una conexión saliente.
[Service] define cómo se ejecuta el programa. Aquí se encuentran la mayoría de las configuraciones:
[Service]
ExecStart=/usr/local/bin/myapp --config /etc/myapp/config.toml
WorkingDirectory=/opt/myapp
User=myapp
Restart=on-failure
RestartSec=5
Environment=LOG_LEVEL=infoUser=myapp ejecuta el programa con una cuenta sin privilegios en lugar de root; esta es la línea más importante para la seguridad. Restart=on-failure y RestartSec=5 tienen su propia sección más abajo, ya que son el motivo principal por el cual se crea un servicio.
[Install] define qué sucede al habilitar el servicio:
[Install]
WantedBy=multi-user.targetWantedBy=multi-user.target es lo que vincula el servicio al arranque cuando se ejecuta systemctl enable. Sin una sección [Install], el servicio puede iniciarse manualmente, pero no se ejecutará automáticamente tras un reinicio.
Actível y monitoree
Después de escribir o editar cualquier archivo de unidad, recargue systemd para que lea los cambios. Luego, habilite e inicie el servicio en un solo paso:
sudo systemctl daemon-reload
sudo systemctl enable --now myapp.servicedaemon-reload es el paso que se suele omitir: systemd almacena los archivos de unidad en caché, por lo que una edición no surte efecto hasta que se recargue. enable --now habilita el servicio para el arranque y lo inicia inmediatamente. Verifique el estado:
sudo systemctl status myapp.service* myapp.service - My application
Loaded: loaded (/etc/systemd/system/myapp.service; enabled)
Active: active (running) since Wed 2026-07-15 22:40:11 UTC; 3s ago
Main PID: 4123 (myapp)Active: active (running) y enabled son los resultados esperados. Para leer la salida del programa, solicite al journal únicamente esta unidad:
sudo journalctl -u myapp.service -fEl -f sigue las líneas nuevas conforme llegan, de forma similar a tail -f. Cualquier dato que su programa escriba en standard output o standard error aparecerá aquí, sin necesidad de configurar el logging.
Reinicio tras un fallo, la razón por la que está aquí
La principal ventaja de un service es que systemd reinicia su programa cuando este falla. Dos líneas lo permiten:
[Service]
Restart=on-failure
RestartSec=5Restart=on-failure reinicia el programa cuando este finaliza con un código distinto de cero o muere por una señal de crash como SIGKILL o SIGSEGV. Un cierre limpio, o una parada mediante SIGTERM, SIGINT, SIGHUP o SIGPIPE, no lo activan. RestartSec=5 espera cinco segundos entre intentos para evitar que un programa que falla instantáneamente entre en un bucle infinito. Confírmelo matando el proceso y observando cómo systemd lo reinicia. Use SIGKILL: el SIGTERM predeterminado cuenta como una parada limpia, por lo que on-failure no reiniciaría el service:
sudo systemctl kill -s SIGKILL myapp.service
sudo systemctl status myapp.serviceEn menos de cinco segundos, el status muestra un nuevo Main PID y active (running) nuevamente. Esa es la función principal, y es la razón por la que un service es superior a ejecutar un programa en tmux o screen.
Ejecútelo como un usuario sin privilegios y refuerce la seguridad
Un servicio que se ejecuta como root puede realizar cualquier acción en su servidor si el programa es explotado. Ejecútelo con su propio usuario y asigne a systemd directivas para limitarlo. Primero, cree una cuenta de sistema sin acceso a login y sin directorio home:
sudo useradd --system --no-create-home --shell /usr/sbin/nologin myappLuego, configure User=myapp y añada líneas de endurecimiento en [Service]:
[Service]
User=myapp
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=trueCada línea elimina una capacidad que el programa no necesita. NoNewPrivileges=true evita que el proceso obtenga nuevos privilegios, incluso mediante un binario setuid. PrivateTmp=true le asigna un /tmp privado que ningún otro proceso puede ver. ProtectSystem=strict establece todo el sistema de archivos como solo lectura, excepto las rutas que especifique con ReadWritePaths=. ProtectHome=true oculta /home por completo. Este es el mismo principio de mínimo privilegio que colocar un servicio tras un firewall: otorgue solo lo necesario. Si ha leído la guía sobre cómo cerrar la brecha del firewall IPv6 en un VPS, esta es la parte local de la misma estrategia. Para un servicio con acceso a internet, combine este endurecimiento con Fail2ban delante de SSH y un firewall con política de denegación por defecto.
En lugar de escribir todo esto manualmente y cometer errores en las directivas, genere una unidad completa y endurecida y cópiela:
Timers: el cron moderno
Un timer de systemd ejecuta un servicio según un horario; es el reemplazo moderno de un cron job. Un timer consta de dos archivos: un .service que realiza el trabajo y un .timer que indica cuándo hacerlo. Supongamos que requiere un backup a las 3am todos los días. El servicio ejecuta la tarea una vez y finaliza:
# /etc/systemd/system/backup.service
[Unit]
Description=Nightly backup
[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.shType=oneshot indica a systemd que el programa se ejecuta, termina y finaliza, en lugar de permanecer residente. El timer lo programa:
# /etc/systemd/system/backup.timer
[Unit]
Description=Run the nightly backup
[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true
[Install]
WantedBy=timers.targetOnCalendar=*-*-* 03:00:00 significa a las 3am todos los días. Pruebe cualquier expresión de calendario con systemd-analyze calendar "*-*-* 03:00:00", que confirma si se procesa correctamente y muestra las próximas ejecuciones. Persistent=true ejecuta un trabajo omitido tan pronto como el servidor se reinicia si estaba apagado a las 3am, algo que cron no puede hacer. Note que un timer se habilita mediante timers.target, no mediante multi-user.target. Habilite el timer, no el servicio:
sudo systemctl daemon-reload
sudo systemctl enable --now backup.timer
systemctl list-timerslist-timers muestra cada timer con su próxima y última ejecución, permitiendo ver de un vistazo cuándo se ejecutará su tarea. El generador anterior crea los archivos .service y .timer emparejados cuando activa el modo timer. Frente a una línea de cron, un timer ofrece logs reales en el journal, las mismas directivas de hardening que cualquier servicio y la recuperación de ejecuciones omitidas que proporciona Persistent=true. Cron sigue siendo útil para tareas simples; un timer es la mejor herramienta para tareas críticas.
FAQ
¿Cuál es la diferencia entre un servicio de systemd y un cron job?
Un servicio mantiene un programa en ejecución continua: se inicia al arrancar el sistema, se reinicia si falla y registra sus logs en el journal. Un cron job ejecuta un comando breve según un horario y luego finaliza. Si necesita programación pero también requiere logs en el journal, hardening y la capacidad de ejecutar tareas pendientes tras un fallo, use un systemd timer. Este combina un horario .timer con un servicio oneshot y sustituye a cron en la mayoría de las tareas del servidor.
¿Dónde debo colocar mi archivo de servicio de systemd?
Coloque sus propias unidades en /etc/systemd/system/, con un nombre que termine en .service. Ese directorio es para unidades añadidas por el administrador y tiene prioridad sobre las unidades de los paquetes en /lib/systemd/system/. Tras crear o editar un archivo allí, ejecute sudo systemctl daemon-reload para que systemd reconozca el cambio.
¿Cómo hago que un servicio se reinicie si falla?
Añada Restart=on-failure y RestartSec=5 a la sección [Service], luego ejecute sudo systemctl daemon-reload y reinicie el servicio. systemd relanza el programa cuando este finaliza con un código distinto de cero o muere por una señal de crash, esperando cinco segundos entre intentos. Pruébelo con sudo systemctl kill -s SIGKILL myapp.service —SIGTERM, la señal por defecto, cuenta como un cierre limpio y no activa on-failure— y observe que systemctl status muestre un nuevo PID en pocos segundos.
¿Cómo ejecuto un servicio de systemd como un usuario sin privilegios de root?
Cree una cuenta de sistema con sudo useradd --system --no-create-home --shell /usr/sbin/nologin myapp y añada User=myapp a la sección [Service]. Añada NoNewPrivileges=true, PrivateTmp=true y ProtectSystem=strict para que el proceso funcione con el mínimo acceso necesario. Ejecutar como un usuario sin privilegios es el cambio más importante para la seguridad de un servicio.
¿Por qué falló el inicio de mi servicio?
Ejecute systemctl status myapp.service para obtener el resumen y journalctl -u myapp.service para ver la salida completa. Las causas más comunes son una ruta incorrecta en ExecStart, la falta de un WorkingDirectory, un error de permisos porque User= no puede leer un archivo, o un sudo systemctl daemon-reload olvidado tras una edición. El journal muestra el mensaje de error propio del programa, que suele indicar el problema directamente.