SSD Nodes Learn Hosting plans →
Guías Matt ConnorPor Matt Connor · Actualizado 2026-08-24

Ejecutar un programa como servicio systemd en un VPS

Aprende a crear una unidad systemd para iniciar tu programa al arrancar, reiniciarlo tras un fallo, consultar el journal, añadir un timer y limitar privilegios.

Qué es un servicio de systemd y por qué necesita uno

Un servicio de systemd es un archivo de texto pequeño que indica al servidor cómo ejecutar un programa: iniciarlo durante el arranque, reiniciarlo si falla y enviar su salida al registro del sistema. Esa es toda su función. Un programa que inicia manualmente en una sesión SSH termina en cuanto cierra la sesión o el servidor se reinicia. Un programa administrado por un servicio de systemd sigue ejecutándose porque el propio servidor lo controla, no el shell.

systemd es el sistema de inicio de Ubuntu, Debian, Fedora y la mayoría de los servidores Linux modernos. Es el primer proceso que se inicia y supervisa todo lo demás. No siempre fue así, y cómo systemd sustituyó a los scripts de inicio que lo precedieron merece una lectura cuando ya sepa qué hace un archivo de unidad. Al escribir un archivo de servicio, entrega el programa a ese supervisor. Esta guía muestra la unidad funcional más pequeña, las tres secciones que tiene toda unidad, cómo activarla y leer sus registros, cómo ejecutarla según una programación con un temporizador y cómo restringirla para que se ejecute con el mínimo de privilegios posible.

El servicio mínimo que funciona

Un archivo de servicio se encuentra en /etc/systemd/system/, termina en .service y sólo necesita 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.target

Esta es una unidad completa y funcional. ExecStart es el comando que se debe ejecutar. WantedBy=multi-user.target significa iniciar este servicio cuando el servidor alcance el funcionamiento multiusuario normal. Esto hace que se inicie durante el arranque. Todo lo demás son ajustes.

Las tres secciones y para qué sirve cada una

Cada archivo de unidad se divide en secciones entre corchetes. Un servicio usa tres.

[Unit] describe el servicio y sus relaciones. Estas son las dos líneas que usará con más frecuencia:

[Unit]
Description=My application
After=network-online.target
Wants=network-online.target

Description es la etiqueta descriptiva que verá en systemctl status. After=network-online.target indica a systemd que no inicie el programa hasta que la red esté disponible. Esto es importante para cualquier programa que escuche en un puerto o realice una conexión saliente.

[Service] define cómo se ejecuta el programa. La mayoría de los ajustes se especifican aquí:

[Service]
ExecStart=/usr/local/bin/myapp --config /etc/myapp/config.toml
WorkingDirectory=/opt/myapp
User=myapp
Restart=on-failure
RestartSec=5
Environment=LOG_LEVEL=info

User=myapp ejecuta el programa con una cuenta sin privilegios en lugar de root. Es la línea más importante para la seguridad. No hay ninguna línea Type=, por lo que systemd recurre a simple y presupone que el proceso ExecStart permanece en primer plano. Un programa que se bifurca para ejecutarse en segundo plano necesita el valor Type= adecuado para su forma de inicio, o la unidad indicará que está activa aunque el daemon real ya haya terminado. Restart=on-failure y RestartSec=5 tienen su propia sección más adelante, porque son el motivo principal por el que se crea un servicio.

[Install] define qué ocurre al habilitar el servicio:

[Install]
WantedBy=multi-user.target

WantedBy=multi-user.target es lo que vincula el servicio al arranque cuando ejecuta systemctl enable. Sin una sección [Install], puede iniciar el servicio manualmente, pero no se iniciará por sí solo después de reiniciar el sistema.

Actívelo y supervise su funcionamiento

Después de escribir o editar cualquier archivo de unidad, vuelva a cargar systemd para que lea el cambio. Luego habilite e inicie el servicio en un solo paso:

sudo systemctl daemon-reload
sudo systemctl enable --now myapp.service

daemon-reload es el paso que se suele olvidar: systemd almacena en caché los archivos de unidad, por lo que una edición no tiene efecto hasta que se vuelve a cargar. enable --now habilita el servicio para el arranque y lo inicia inmediatamente. Compruebe el resultado:

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 estados esperados. Para leer la salida del programa, consulte el journal sólo para esta unidad:

sudo journalctl -u myapp.service -f

-f sigue las líneas nuevas a medida que llegan, como tail -f. Todo lo que el programa escriba en la salida estándar o en la salida de error estándar aparece aquí, sin que tenga que configurar el registro.

Reinicio tras un fallo: el motivo por el que está aquí

La principal ventaja de un servicio es que systemd reinicia el programa cuando termina de forma inesperada. Dos líneas lo hacen:

[Service]
Restart=on-failure
RestartSec=5

Restart=on-failure reinicia el programa cuando termina con un código distinto de cero o muere debido a una señal de fallo, como SIGKILL o SIGSEGV. Una salida normal, o una detención mediante SIGTERM, SIGINT, SIGHUP o SIGPIPE, no lo activa. RestartSec=5 espera cinco segundos entre intentos, para que un programa que falla inmediatamente no entre en un bucle continuo. Confírmelo eliminando el proceso y observando cómo systemd lo inicia de nuevo. Use SIGKILL: el SIGTERM predeterminado cuenta como una detención normal, por lo que on-failure no reiniciaría el servicio:

sudo systemctl kill -s SIGKILL myapp.service
sudo systemctl status myapp.service

En un plazo de cinco segundos, el estado muestra un nuevo Main PID y active (running) de nuevo. Esa es toda la funcionalidad y explica por qué un servicio es mejor que dejar un programa ejecutándose en tmux o screen.

Ejecútelo con un usuario sin privilegios y refuércelo

Un servicio que se ejecuta como root puede hacer cualquier cosa en el servidor si alguien explota el programa. Ejecútelo con su propio usuario y añada varias directivas de systemd para aislarlo. Primero cree una cuenta del sistema sin inicio de sesión y sin directorio personal:

sudo useradd --system --no-create-home --shell /usr/sbin/nologin myapp

A continuación, establezca User=myapp y añada las líneas de refuerzo a [Service]:

[Service]
User=myapp
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true

Cada línea elimina una capacidad que el programa no necesita. NoNewPrivileges=true impide que el proceso obtenga nuevos privilegios, incluso mediante un binario setuid. PrivateTmp=true le proporciona un /tmp privado que ningún otro proceso puede ver. ProtectSystem=strict hace que todo el sistema de archivos sea de sólo lectura, excepto algunas rutas que especifique con ReadWritePaths=. ProtectHome=true oculta /home por completo al proceso. Es el mismo principio de mínimo privilegio que aplicar un firewall delante de un servicio: concédale sólo lo que necesita. Si ha leído la guía sobre cerrar la brecha del firewall IPv6 en un VPS, esta es la parte del host de la misma idea. Para un servicio expuesto a Internet, combine este refuerzo con Fail2ban delante de SSH y un firewall con denegación predeterminada.

En lugar de escribir todo esto manualmente y recordar mal una directiva, genere una unidad completa y reforzada y cópiela:

Toolsystemd service and timer generator

Temporizadores: el cron moderno

Un temporizador de systemd ejecuta un servicio según un calendario y es el reemplazo moderno de una tarea de cron. Un temporizador consta de dos archivos: un .service que realiza el trabajo y un .timer que indica cuándo ejecutarlo. Suponga que quiere realizar una copia de seguridad todos los días a las 3am. El servicio ejecuta la tarea una vez y termina:

# /etc/systemd/system/backup.service
[Unit]
Description=Nightly backup

[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh

Type=oneshot indica a systemd que el programa se ejecuta, termina y finaliza, en lugar de permanecer residente. El temporizador programa su ejecución:

# /etc/systemd/system/backup.timer
[Unit]
Description=Run the nightly backup

[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true

[Install]
WantedBy=timers.target

OnCalendar=*-*-* 03:00:00 significa las 3am todos los días. Pruebe cualquier expresión de calendario con systemd-analyze calendar "*-*-* 03:00:00". Este comando confirma que la expresión se puede analizar e imprime las próximas horas de ejecución. Persistent=true ejecuta una tarea omitida en cuanto el servidor vuelve a estar disponible si estaba apagado a las 3am, algo que cron no puede hacer. Observe que un temporizador se habilita mediante timers.target, no mediante multi-user.target. Habilite el temporizador, no el servicio:

sudo systemctl daemon-reload
sudo systemctl enable --now backup.timer
systemctl list-timers

list-timers muestra todos los temporizadores con su próxima ejecución y la última ejecución. Así puede comprobar de un vistazo cuándo se ejecutará de nuevo la tarea. El generador anterior crea los .service y .timer asociados cuando se activa el modo de temporizador. Frente a una línea de cron, un temporizador ofrece registros reales en el journal, las mismas directivas de refuerzo que cualquier servicio y la recuperación de ejecuciones omitidas que proporciona Persistent=true. Cron sigue siendo adecuado para una tarea sencilla. Un temporizador es una opción mejor cuando la tarea es importante.

FAQ

¿Cuál es la diferencia entre un servicio de systemd y una tarea de cron?

Un servicio mantiene activo un programa de larga duración: se inicia durante el arranque, se reinicia si falla y registra los mensajes en el journal. Una tarea de cron ejecuta un comando breve según un horario y después termina. Si necesita programación, pero también registros en el journal, endurecimiento y recuperación de ejecuciones omitidas, use un timer de systemd. Este combina una programación .timer con un servicio oneshot y sustituye a cron en la mayoría de las tareas del servidor.

¿Dónde coloco el archivo de servicio de systemd?

Coloque sus propias unidades en /etc/systemd/system/, con un nombre que termine en .service. Ese directorio está destinado a las unidades que añade el administrador y tiene prioridad sobre las unidades distribuidas por los paquetes en /lib/systemd/system/. Después de crear o editar un archivo allí, ejecute sudo systemctl daemon-reload para que systemd detecte el cambio.

¿Cómo hago que un servicio se reinicie si se bloquea?

Añada Restart=on-failure y RestartSec=5 a la sección [Service]. Después, ejecute sudo systemctl daemon-reload y reinicie el servicio. systemd vuelve a iniciar el programa cuando termina con un código distinto de cero o muere por una señal de bloqueo, y espera cinco segundos entre intentos. Pruébelo con sudo systemctl kill -s SIGKILL myapp.service. SIGTERM, la señal predeterminada, cuenta como una detención limpia y no activa on-failure. Compruebe que systemctl status muestre un PID nuevo en unos segundos.

¿Cómo ejecuto un servicio de systemd con un usuario que no sea root?

Cree una cuenta de sistema con sudo useradd --system --no-create-home --shell /usr/sbin/nologin myapp. Después, añada User=myapp a la sección [Service]. Añada NoNewPrivileges=true, PrivateTmp=true y ProtectSystem=strict para que el proceso se ejecute con los permisos mínimos necesarios. Ejecutar el servicio con un usuario sin privilegios es el cambio individual más importante que puede aplicar a su seguridad.

¿Por qué no se inició mi servicio?

Ejecute systemctl status myapp.service para ver el resumen y journalctl -u myapp.service para ver toda la salida. Las causas más habituales son una ruta incorrecta en ExecStart, la ausencia de WorkingDirectory, un error de permisos porque User= no puede leer un archivo o la falta de sudo systemctl daemon-reload después de editar la configuración. El journal muestra el mensaje de error del propio programa, que normalmente identifica el problema directamente.