Dejar un proceso corriendo en el VPS
Lo que lanzás desde una terminal se termina con la terminal. Para algo que dura un rato, como una migración o una importación, alcanza con tmux o screen: dejás el proceso adentro de la sesión, te desenganchás y volvés más tarde. Para algo que tiene que estar siempre no alcanza, porque una sesión no sobrevive a un reinicio ni se levanta sola si el proceso se cae: eso es una unidad de systemd, que se escribe una vez en /etc/systemd/system, se habilita con systemctl enable y deja todo su registro en journalctl.
Por qué se muere al cerrar la ventana
Cuando abrís una sesión SSH, el intérprete de comandos que te atiende es el padre de todo lo que lances desde ahí. Al cerrar la terminal, o al cortarse la conexión, el sistema avisa que esa terminal se fue y los procesos que colgaban de ella terminan. Mandarlo al fondo con un ampersand no cambia nada, porque sigue colgando de la misma sesión. nohup sí evita ese aviso, pero no resuelve el resto: no lo podés volver a mirar, no se reinicia si se cae y desaparece en el primer reinicio del servidor.
tmux, la sesión que te espera
- Instalalo si hace falta: sudo apt install tmux.
- Abrí una sesión con nombre: tmux new -s migracion. La terminal cambia apenas, con una barra abajo, y ya estás adentro.
- Lanzá ahí el comando largo y desenganchate sin cortar nada: apretá Ctrl-b, soltá, y después d.
- Ya podés cerrar todo e irte. Cuando vuelvas, tmux ls lista las sesiones y tmux attach -t migracion te devuelve la salida como si nunca te hubieras ido.
- Cuando el trabajo terminó, exit cierra la sesión. Si quedó colgada, tmux kill-session -t migracion la elimina.
screen, y el error que aparece siempre
screen hace lo mismo y sigue instalado en muchos servidores. Se abre con screen -S migracion, el prefijo es Ctrl-a en lugar de Ctrl-b, y se desengancha con Ctrl-a seguido de d. screen -ls lista las sesiones y screen -r migracion vuelve a entrar.
El problema clásico es que se corte el SSH sin que llegues a desengancharte: la sesión sigue viva con el proceso adentro, pero figura conectada a una terminal que ya no existe y screen -r se niega a devolvértela. La salida es screen -d -r migracion, que la desengancha de esa terminal fantasma y después te la entrega.
Hasta dónde llega una sesión
| Qué pasa | tmux o screen | Unidad de systemd |
|---|---|---|
| Cerrás la terminal | Sigue corriendo | Sigue corriendo |
| Se corta el SSH | Sigue corriendo | Sigue corriendo |
| Reiniciás el servidor | Se pierde | Arranca sola |
| El proceso se cae | Queda muerto | Se reinicia |
| Dónde queda la salida | En pantalla, hasta que se llena | En el journal, con fecha |
La regla es simple: si el proceso tiene que estar funcionando cuando vos no estás, no es una sesión, es un servicio.
Una unidad mínima, línea por línea
| Línea | Qué hace |
|---|---|
| [Unit] | Empieza la sección de descripción y dependencias |
| Description=Mi aplicación | El texto que vas a ver en systemctl status |
| Wants=network-online.target | Pide que la red esté configurada antes |
| After=network-online.target | Y ordena el arranque después de eso |
| [Service] | Empieza la sección del proceso en sí |
| User=juan | Con qué cuenta corre. Sin esta línea, root |
| WorkingDirectory=/home/juan/miapp | Desde qué carpeta arranca |
| ExecStart=/usr/bin/node /home/juan/miapp/server.js | El comando, con la ruta completa del ejecutable |
| Restart=on-failure | Lo vuelve a levantar si termina mal |
| RestartSec=5 | Espera cinco segundos antes de cada reintento |
| [Install] | Empieza la sección que define cuándo arranca solo |
| WantedBy=multi-user.target | Hace que arranque con el sistema al habilitarlo |
El archivo va en /etc/systemd/system/miapp.service y el nombre del archivo es el nombre del servicio.
Habilitarla y ponerla en marcha
- Creá el archivo con sudo nano /etc/systemd/system/miapp.service y pegá las líneas de arriba, cada sección con su encabezado.
- Avisale a systemd que hay un archivo nuevo: sudo systemctl daemon-reload. No lo relee solo, y saltear este paso es el motivo por el que parece que los cambios no tienen efecto.
- Habilitala y arrancala de una: sudo systemctl enable --now miapp. Habilitar es para el próximo arranque; el --now además la inicia ahora.
- Mirá cómo quedó con systemctl status miapp: tiene que decir que está activa, y abajo aparecen las últimas líneas del registro.
- Reiniciá el servidor una vez y volvé a mirar el estado. Es la única prueba que confirma que va a levantar sola.
- Para cambiarla después, sudo systemctl edit miapp crea un agregado sin tocar el original. Al terminar, daemon-reload y restart.
Leer los registros
- journalctl -u miapp -f muestra lo que va pasando en vivo: es lo que querés tener abierto mientras probás.
- journalctl -u miapp -n 100 --no-pager tira las últimas cien líneas de una y devuelve la terminal.
- journalctl -u miapp --since "today" acota al día y --since "-1h" a la última hora; -p err deja solo los errores, que es por donde empezar cuando el registro es largo.
- journalctl -u miapp -b -1 muestra el arranque anterior, útil para ver por qué se reinició el servidor. Funciona si el journal se guarda en disco, en /var/log/journal; si esa carpeta no existe, el registro vive en memoria y se pierde al reiniciar.
- Todo lo que tu programa imprima en pantalla termina en el journal con fecha y nombre del servicio, sin redirigir nada a un archivo ni inventar una rotación.
Que se levante sola, pero con límite
Restart=on-failure la vuelve a levantar cuando termina mal: código de salida distinto de cero, una señal que la mata, un tiempo de espera agotado. Lo que no cubre es el programa que termina bien y se va; si el tuyo sale limpio y aun así querés que vuelva, va Restart=always.
systemd tampoco reintenta para siempre, y es a propósito: de fábrica, si una unidad arranca más de cinco veces en diez segundos deja de intentarlo y queda marcada como fallida, con un mensaje sobre reintentos demasiado seguidos. Por eso RestartSec=5, que separa los intentos para no chocar contra ese límite y le da tiempo a lo que falló. Cuando arreglaste la causa, el contador se limpia con sudo systemctl reset-failed miapp.
Los errores que aparecen siempre
- Ruta relativa en ExecStart. Va la ruta completa del ejecutable, /usr/bin/node y no node; cuando falla, el estado dice que no pudo ejecutarlo. which node, corrido con el usuario del servicio, te la da, y si el intérprete vino de un gestor de versiones instalado en el home, esa ruta no es /usr/bin.
- Sintaxis del intérprete de comandos. Las tuberías, los redireccionamientos y las variables no funcionan en ExecStart porque no hay un intérprete en el medio: si los necesitás, el comando entero va dentro de /bin/sh -c.
- Sin User=, el servicio corre como root. Cuando agregás la línea, revisá que las carpetas y los archivos de la aplicación pertenezcan a ese usuario, o va a fallar al escribir.
- Las variables de entorno de tu sesión no existen para el servicio: hay que declararlas con Environment=, o en un archivo aparte con EnvironmentFile= y permisos 600 si adentro hay contraseñas.
- Un programa que se manda solo al fondo. systemd ve terminar al proceso que lanzó y da el servicio por muerto; la solución es dejarlo en primer plano, que casi siempre es una opción del propio programa.
Preguntas frecuentes
¿nohup no alcanza?
Alcanza para que un comando sobreviva a que cierres la terminal, y nada más: no lo podés volver a mirar, no se reinicia si falla y no vuelve después de un reinicio.
¿tmux o screen?
Para empezar de cero, tmux: se mantiene, divide la pantalla con más comodidad y lo que busques va a estar explicado en esos términos. screen sirve igual y tiene la ventaja de estar ya instalado en servidores viejos.
¿Cómo hago que arranque después de la base de datos?
Con After= y el nombre de la unidad de la base, que te lo dice systemctl list-units --type=service. Ojo con lo que garantiza: After= ordena el arranque pero no espera a que la base acepte consultas. Para ese hueco sirve más Restart=on-failure con unos segundos de espera, que reintenta hasta que responde.
¿Y si mi aplicación ya escribe su propio archivo de registro?
No hay conflicto: seguí usándolo para el detalle y dejá que lo que salga por pantalla vaya al journal. Lo que conviene evitar es duplicar lo mismo en dos lugares, y recordar que el archivo propio necesita su rotación, cosa que el journal resuelve solo.
Seguí leyendo
Primeros pasos al recibir un VPS: la checklist de seguridad
Un servidor nuevo está expuesto desde el minuto uno. Las diez cosas que hay que hacer antes de instalar nada.
Diagnosticar una conexión lenta: red, servidor o sitio
Ping, mtr y una medición con curl alcanzan para saber de quién es el problema. Cómo se lee un salto con pérdida y qué datos mandar por ticket.
Qué es un VPS y cuándo conviene dar el salto
Explicado sin jerga: qué te dan realmente cuando contratás un VPS, qué queda a tu cargo y en qué momento deja de ser opcional.