Gratis Te mudamos el sitio y los correos sin cortes ni costo

0810 345 6377 ESEN, cambiar a inglés
InHosting
ESEN, cambiar a inglés

Servidores Linux

Cómo dejar un proceso corriendo en el VPS después de cerrar SSH

Por el equipo técnico de InHosting7 min de lectura

En pocas palabras

Todo lo que lanzás en una sesión SSH depende de esa sesión: si se corta la conexión, el proceso recibe la señal de cierre y termina. Para un trabajo de horas que querés poder seguir mirando, usá tmux o screen: el proceso queda dentro de una sesión a la que volvés a conectarte. Para una tarea única que no necesitás mirar, systemd-run la convierte en un servicio temporal con su registro. Y para un programa que tiene que estar siempre, escribí una unidad de systemd: arranca con el servidor, se reinicia si se cae y guarda su salida en journalctl.

Por qué el proceso muere cuando se corta la conexión

Al entrar por SSH, el sistema te asigna una terminal y un intérprete de comandos, y todo lo que lanzás cuelga de ahí. Cuando la conexión se cae, esa terminal desaparece y el sistema les manda a sus procesos la señal SIGHUP, que por defecto los termina. Agregar & al final del comando solo lo manda al fondo: sigue siendo hijo de la misma sesión.

nohup hace que el proceso ignore esa señal, y por eso sobrevive al corte. Pero es una solución a medias: la salida va a parar a un archivo nohup.out, no podés volver a interactuar con el proceso, nadie lo levanta si falla y un reinicio del servidor lo borra del mapa.

Qué herramienta usar en cada caso

Hay cuatro situaciones y cada una tiene su herramienta. La última, las tareas periódicas, tiene guía propia en programar tareas con cron y systemd.

Lo que necesitásHerramienta¿Sobrevive a un reinicio?
Un comando largo que querés seguir mirando: una importación, un rsynctmux o screenNo
Una tarea única que no hace falta mirarsystemd-runNo, pero queda registrada
Un programa que tiene que estar siempre: una API, un bot, un workerUnidad de systemdSí, arranca solo
Algo que corre cada cierto tiempoTemporizador de systemd o cronSí

tmux: una sesión que te espera

tmux mantiene una terminal viva en el servidor, independiente de tu conexión. Te desenganchás, cerrás la notebook y al día siguiente encontrás todo como lo dejaste, con la salida en pantalla. Está en los repositorios de todas las distribuciones: sudo apt install tmux en Debian y Ubuntu, sudo dnf install tmux en AlmaLinux.

tmux new -s importacion            # abre una sesión con nombre
# ... lanzás el comando largo ...
# Ctrl-b y después d: te desenganchás sin cortar nada
tmux ls                             # lista las sesiones abiertas
tmux attach -t importacion          # volvés a entrar
tmux kill-session -t importacion    # la cerrás cuando terminó
  • Para ver lo que ya pasó por pantalla, Ctrl-b y después [ activa el modo de desplazamiento; con q salís.
  • Ctrl-b % divide la ventana en dos, útil para tener htop al lado del proceso.
  • Si en el servidor ya está screen, sirve igual con otros atajos: screen -S importacion para abrir, Ctrl-a d para desengancharte y screen -r importacion para volver. Si la conexión se cortó antes de desengancharte y screen dice que la sesión está ocupada, screen -d -r importacion la libera y te la devuelve.

systemd-run: un servicio de un solo uso

Menos conocido y muy práctico: systemd-run lanza un comando como servicio temporal, fuera de tu sesión. Sobrevive al corte de SSH, su salida queda en el journal con fecha y hora, y cuando termina, desaparece solo. Es ideal para una copia grande o una conversión que no necesitás mirar en vivo. Con --uid= elegís con qué usuario corre.

sudo systemd-run --unit=copia-fotos rsync -a /srv/fotos/ /mnt/respaldo/fotos/
systemctl status copia-fotos      # mientras corre
journalctl -u copia-fotos -f      # la salida, en vivo o cuando ya terminó

Una unidad de systemd para lo que tiene que estar siempre

Supongamos un worker en Python que procesa una cola, instalado en /opt/cola con su entorno virtual, que corre con un usuario sin privilegios llamado deploy (si todavía hacés todo como root, antes mirá cómo crear un usuario con sudo). La unidad va en /etc/systemd/system/cola.service, y el nombre del archivo es el nombre del servicio:

[Unit]
Description=Worker de la cola de pedidos
Wants=network-online.target
After=network-online.target mariadb.service
[Service]
User=deploy
WorkingDirectory=/opt/cola
EnvironmentFile=/etc/cola.env
ExecStart=/opt/cola/venv/bin/python worker.py
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
  • After= con el nombre de la base hace que arranque después de ella, pero no espera a que acepte conexiones. Si el worker falla por eso, Restart=on-failure lo reintenta cada cinco segundos hasta que la base responde.
  • EnvironmentFile= carga variables como contraseñas o direcciones de APIs desde un archivo aparte. Dale permisos 600 y dejalo con dueño root: systemd lo lee antes de pasar al usuario del servicio.
  • ExecStart= usa la ruta completa del intérprete del entorno virtual. systemd no lee tu .bashrc ni activa entornos, y solo encuentra por nombre los programas de las carpetas estándar como /usr/bin; Node instalado con nvm o cualquier cosa de tu carpeta personal va con la ruta entera.
  • Sin User=, el servicio corre como root. Con User=deploy, la carpeta /opt/cola y todo lo que escriba el programa tienen que pertenecer a ese usuario.

Activarla y probar que vuelve sola

  1. Avisale a systemd que hay un archivo nuevo o modificado: sudo systemctl daemon-reload.
  2. Habilitala para el arranque e iniciala en el mismo paso: sudo systemctl enable --now cola.
  3. Revisá el estado con systemctl status cola. Tiene que decir active (running) y mostrar las últimas líneas de la salida.
  4. Probá el reinicio automático matando el proceso a propósito con sudo systemctl kill -s KILL cola. A los cinco segundos, el estado tiene que volver a active.
  5. Reiniciá el servidor con sudo reboot y, al volver, confirmá con systemctl is-active cola. Es la única prueba de que arranca sola.

Leer la salida en journalctl

Todo lo que el programa escribe en pantalla, errores incluidos, queda en el journal con fecha, hora y nombre del servicio. No hace falta redirigir a un archivo ni armar una rotación.

journalctl -u cola -f                      # en vivo, mientras probás
journalctl -u cola --since "30 min ago"    # la última media hora
journalctl -u cola -n 50 --no-pager        # las últimas 50 líneas
journalctl -u cola -b -1                   # lo que pasó en el arranque anterior
  • La última opción necesita que el journal se guarde en disco. Si no existe la carpeta /var/log/journal, el registro vive en memoria y se pierde al reiniciar; se activa con sudo mkdir -p /var/log/journal y sudo systemctl restart systemd-journald.
  • journalctl --disk-usage muestra cuánto ocupa, y sudo journalctl --vacuum-time=30d borra lo que tenga más de un mes.

Cuando no arranca: qué dice el estado

Si systemctl status muestra failed, la línea del proceso principal trae un código que casi siempre alcanza para saber qué pasó:

Código en el estadoQué significaQué revisar
status=203/EXECNo pudo ejecutar el comandoLa ruta de ExecStart, que el archivo exista y tenga permiso de ejecución
status=217/USEREl usuario de User= no existeEl nombre, con id deploy
status=200/CHDIRNo pudo entrar a WorkingDirectoryQue la carpeta exista y el usuario tenga permiso
status=1/FAILUREEl programa arrancó y terminó con errorLa salida en journalctl: el error es del programa, no de systemd
start-limit-hitSe reinició demasiadas veces seguidasLa causa del error; después, sudo systemctl reset-failed cola

systemd deja de reintentar si una unidad arranca más de 5 veces en 10 segundos. Con RestartSec=5 no se llega a ese límite; sin esa línea, los reintentos son casi inmediatos y se llega enseguida.

Dos trampas que no dejan un código claro

La primera: tuberías, redirecciones y variables del intérprete (|, >, $HOME) no funcionan en ExecStart, porque systemd ejecuta el programa directamente, sin un intérprete de comandos en el medio. Si los necesitás, el comando entero va dentro de /bin/sh -c "...".

La segunda: un programa que se manda solo al fondo. systemd ve terminar el proceso que lanzó y da el servicio por muerto, o lo reinicia en un ciclo. Casi todos los programas tienen una opción para quedarse en primer plano, y es la que hay que usar.

Preguntas frecuentes

¿Cómo paro el servicio sin que vuelva en el próximo reinicio?

sudo systemctl disable --now cola lo detiene y lo saca del arranque en un solo paso. Si solo querés pararlo un rato, sudo systemctl stop cola: queda quieto hasta que lo arranques o reinicies el servidor.

¿Puedo limitar cuánta memoria usa el servicio?

Sí. Con sudo systemctl edit cola agregá, bajo el encabezado [Service], la línea MemoryMax=512M, y reiniciá el servicio. El cambio queda en un archivo aparte, sin tocar el original. Si el programa pasa el límite, el sistema lo corta y Restart= lo vuelve a levantar. systemctl status muestra el consumo actual.

¿Si uso Docker necesito todo esto?

Para los contenedores, no: Docker los levanta solo si los creaste con --restart unless-stopped y el servicio docker está habilitado. Lo que no conviene es mezclar una política de reinicio de Docker con una unidad de systemd que arranca el mismo contenedor, porque terminan peleándose.

¿Puedo dejar una aplicación en producción dentro de tmux?

Funciona hasta el primer reinicio del servidor o la primera caída del programa, y ahí nadie la levanta. Para producción, unidad de systemd; tmux es para el trabajo que estás haciendo vos en ese momento.

Seguí leyendo

¿Preferís que lo veamos juntos? Te atiende una persona.

Si la guía no alcanza para tu caso, contanos qué necesitás: te ayudamos a elegir el plan, a migrar o a configurar tu servidor.

Hablar con un técnico