EQVPS
PanelEmpezar

Cómo programar tareas en un VPS con cron y temporizadores de systemd

Ejecuta copias de seguridad, limpiezas y scripts según un horario. Cron en cinco minutos con las tres trampas que rompen tareas en silencio, y luego los temporizadores de systemd para registros, recuperación tras una caída y hora exacta. Cada comportamiento de abajo se probó en un servidor Ubuntu 24.04 nuevo.

Un servidor se gana el sueldo haciendo cosas mientras duermes: copias de seguridad, rotación de registros, renovación de certificados, un informe a las nueve. Linux tiene dos planificadores para eso. Cron tiene cuarenta años y ocupa una línea. Los temporizadores de systemd ocupan dos archivos pequeños y a cambio te dan registros, recuperación tras una caída y hora exacta. Esta guía cubre ambos en Ubuntu 24.04, con cada comportamiento comprobado en un servidor nuevo de nuestra imagen, incluidos los tres errores que hacen que una tarea de cron falle sin decir nada.

Necesitas un usuario con sudo; la guía de configuración de Ubuntu crea uno.

1. Cron en cinco minutos

Cron está instalado y en marcha en nuestras imágenes de Ubuntu y Debian. Cada usuario tiene una tabla de tareas; edita la tuya con:

crontab -e

Cada línea tiene cinco campos de tiempo y un comando:

# min hour day month weekday  command
  30  3    *   *     *        /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1
  */5 *    *   *     *        /usr/local/bin/check.sh
  0   9    *   *     1-5      /usr/local/bin/report.sh
  @reboot                     /usr/local/bin/start-tunnel.sh

Es decir: a las 03:30 cada noche, cada 5 minutos, a las 09:00 entre semana y una vez después de cada arranque. */5 significa uno de cada cinco valores, 1-5 un rango, 1,15 una lista. Se aplica la zona horaria del servidor, UTC en nuestras imágenes; configura la tuya una vez con la guía de nombre de host y zona horaria y cron la sigue.

Comprueba lo que se ejecutó con journalctl -u cron. Cada ejecución registra el usuario y el comando, y ahí aparecen las tres trampas.

Trampa 1: la salida desaparece. Cron envía por correo la salida de una tarea al usuario. Nuestra imagen no tiene servidor de correo, así que el journal decía (CRON) info (No MTA installed, discarding output) y la salida se perdía. Redirígela tú, como hace el primer ejemplo con >> /var/log/backup.log 2>&1.

Trampa 2: el signo de porcentaje. En un crontab, % termina el comando y convierte el resto en entrada estándar. Programamos date +%F > /tmp/today. El journal muestra lo que cron ejecutó de verdad: CMD (date +). No se escribió ningún archivo ni nos llegó ningún error. Escribe \%, o pon el comando en un script y programa el script.

Trampa 3: un entorno distinto. Cron ejecuta las tareas con /bin/sh, no con bash, en tu directorio personal y con un entorno reducido. En Ubuntu 24.04 el PATH resultó mejor que su fama: la ruta completa del sistema, incluidos /usr/local/bin y /snap/bin, para root y para un usuario normal. Lo que sigue faltando es todo lo de tu directorio personal: ~/.local/bin, el node de nvm, pipx, cargo. Usa rutas completas en la tarea o empieza la línea con . $HOME/.profile;.

Las tareas que pertenecen al sistema y no a un usuario van en /etc/cron.d/, como archivo con un campo extra, el usuario:

30 3 * * * root /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1

Ubuntu también ejecuta todo lo que haya en /etc/cron.hourly, cron.daily, cron.weekly y cron.monthly, a los 17 minutos de cada hora, a las 06:25, a las 06:47 los domingos y a las 06:52 el día 1. Deja ahí un script ejecutable y correrá con los demás.

2. Temporizadores de systemd

Un temporizador son dos unidades: un servicio que hace el trabajo y un temporizador que dice cuándo. Crea primero el script y luego las unidades.

sudo nano /usr/local/bin/backup.sh
sudo chmod +x /usr/local/bin/backup.sh
sudo nano /etc/systemd/system/backup.service
[Unit]
Description=Nightly backup

[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
sudo nano /etc/systemd/system/backup.timer
[Unit]
Description=Run the backup every night at 03:30

[Timer]
OnCalendar=*-*-* 03:30
Persistent=true
AccuracySec=1m

[Install]
WantedBy=timers.target

Activa el temporizador, no el servicio:

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

list-timers muestra la próxima ejecución, cuánto falta y la última ejecución. Los registros no necesitan configuración: journalctl -u backup.service tiene cada inicio, cada final y cada línea de salida, con marcas de tiempo. Para ejecutar la tarea ahora mismo, sudo systemctl start backup.service; así también pruebas el script antes de fiarte del horario.

Sintaxis del calendario. OnCalendar se lee díasemana año-mes-día hora:minuto:segundo, con * para cualquiera. Prueba una expresión antes de usarla:

systemd-analyze calendar "Mon..Fri *-*-* 03:00" --iterations 2

Imprime la forma normalizada y las próximas fechas. Algunas expresiones que comprobamos:

OnCalendarSignificado
*-*-* 03:30Cada día a las 03:30
Mon..Fri *-*-* 09:00Entre semana a las 09:00
*:0/15Cada 15 minutos
weeklyLos lunes a las 00:00
*-*-01 04:00El día 1 de cada mes a las 04:00
*-*-* 03:00 Europe/Kyiv03:00 hora de Kyiv; se ejecutó a las 01:00 UTC en nuestra prueba

Los tres ajustes que conviene conocer. Persistent=true guarda la última ejecución en disco y recupera una ejecución perdida mientras el servidor estaba apagado o el temporizador detenido. Detuvimos nuestro temporizador de cada dos minutos, esperamos a que pasara una ejecución programada y lo volvimos a iniciar: la ejecución perdida saltó en el mismo segundo. Sin Persistent, simplemente espera al siguiente hueco.

AccuracySec es la sorpresa. Por defecto systemd agrupa los temporizadores en una ventana de un minuto para ahorrar despertares, así que nuestra primera ejecución llegó a las 07:42:18, no a las 07:42:00. Con AccuracySec=1s las siguientes cayeron a las 07:46:00 y 07:48:00 al segundo. Ajústalo cuando importe el minuto; deja el valor por defecto para una copia nocturna.

RandomizedDelaySec=10m hace lo contrario a propósito: reparte el inicio en una ventana para que diez servidores no golpeen el mismo almacenamiento de copias en el mismo segundo.

Para una tarea puntual, ningún archivo:

sudo systemd-run --on-active=30m --unit=cleanup-once /usr/local/bin/cleanup.sh

Crea un temporizador transitorio que se dispara una vez y desaparece; hasta entonces lo muestra systemctl list-timers.

3. Cuál usar

CronTemporizador systemd
Configuración1 línea2 archivos
RegistrosSolo si rediriges la salidajournalctl -u name.service
Ejecución perdida con el servidor apagadoPerdidaSe ejecuta al arrancar con Persistent=true
Tarea aún en marcha en el siguiente ticArranca una segunda copiaSe omite hasta que termina la primera
Hora exactaAl minutoAl segundo con AccuracySec=1s
EstadoNingunolist-timers, systemctl status

Usa cron para una línea rápida que reconocerás dentro de un año. Usa un temporizador para todo aquello cuyo fallo necesitas ver, que no debe solaparse o que debe ejecutarse tras un reinicio que se perdió: copias de seguridad, renovación de certificados, limpieza de bases de datos. Si una tarea de cron puede solaparse, envuélvela: flock -n /tmp/backup.lock /usr/local/bin/backup.sh, y la segunda copia termina al instante.

Ambos planificadores ejecutan una tarea y se detienen. Para un programa que debe funcionar sin parar y volver tras un fallo, necesitas un servicio, no un horario; consulta cómo crear un servicio de systemd y, para Windows, cómo ejecutar un script 24/7 en Windows Server. Un buen primer temporizador es el de nuestra guía de copias con restic; las actualizaciones de seguridad automáticas son un temporizador que Ubuntu ya trae, apt-daily-upgrade.timer, uno de los 17 que funcionan en un servidor nuevo.

Preguntas frecuentes

¿Por qué mi tarea funciona en la terminal pero no en cron?

Tres motivos habituales. Cron ejecuta la tarea con sh, no con bash, así que la sintaxis exclusiva de bash falla. Las herramientas instaladas en tu directorio personal, como nvm, pipx o cargo, no están en el PATH de cron, así que usa rutas completas. Y cualquier % en la línea de comando termina el comando: date +%F se convirtió en date + en nuestro servidor. Escápalo como \% o mueve el comando a un script.

¿Adónde va la salida de cron?

Al correo, por defecto. Nuestra imagen no tiene servidor de correo, así que cron registra 'No MTA installed, discarding output' y la salida se pierde. Añade >> /var/log/myjob.log 2>&1 a la tarea, o pásate a un temporizador de systemd, cuya salida llega sola al journal.

¿Qué ventaja tiene un temporizador de systemd frente a cron?

Registros en journalctl, sin segunda copia de una tarea que sigue en marcha, recuperación con Persistent=true de las ejecuciones perdidas mientras el servidor estaba apagado, y una línea de estado con la próxima y la última ejecución. Cron gana en brevedad: una línea en vez de dos archivos.

Mi temporizador se disparó 18 segundos tarde. ¿Por qué?

Por defecto systemd agrupa los temporizadores en una ventana de un minuto para ahorrar despertares, que es el ajuste AccuracySec. Nuestro temporizador de cada dos minutos se disparó a las 07:42:18 en lugar de a las 07:42:00. Con AccuracySec=1s se disparó a las 07:46:00 exactas. Ajústalo cuando importe el minuto exacto.

¿Qué zona horaria usan cron y los temporizadores?

La del servidor, que es UTC en nuestras imágenes. Configúrala una vez con timedatectl, consulta nuestra guía de nombre de host y zona horaria, y tanto cron como los temporizadores la siguen. Un temporizador también puede llevar su propia zona: OnCalendar=*-*-* 03:00 Europe/Kyiv se ejecutó a las 01:00 UTC en nuestra prueba.

Comentarios

Aún no hay comentarios. Sé el primero.

Deja un comentario

Los comentarios se moderan antes de aparecer.