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

Programar tareas en Linux con cron y con timers de systemd

Por el equipo técnico de InHosting8 min de lectura

En pocas palabras

Para que un comando corra solo a una hora fija, abrí tu crontab con crontab -e y agregá una línea con cinco campos (minuto, hora, día del mes, mes y día de la semana) seguidos del comando con su ruta completa: 15 2 * * * /opt/scripts/limpieza.sh corre todos los días a las 2:15. Mandá la salida a un archivo, escribí cada % como \% y revisá la zona horaria del servidor. Si la tarea tiene que correr aunque el equipo haya estado apagado a esa hora, o querés ver su resultado con journalctl, usá un timer de systemd.

Una línea de cron, campo por campo

Cada línea del crontab es una tarea. Tomá esta, que genera un informe los días hábiles a las 7:45:

45 7 * * 1-5 /opt/scripts/informe.sh
PosiciónCampoValoresEn el ejemplo
1Minuto0 a 5945
2Hora0 a 237
3Día del mes1 a 31*, cualquiera
4Mes1 a 12, o jan a dec*, cualquiera
5Día de la semana0 a 7; el 0 y el 7 son domingo1-5, de lunes a viernes
6ComandoCon ruta completa/opt/scripts/informe.sh

Los símbolos que arman cualquier horario

  • * significa todos los valores del campo.
  • La coma arma una lista: 0 8,13,18 * * * corre a las 8, a las 13 y a las 18.
  • El guion, un rango: 1-5 en el día de la semana es de lunes a viernes.
  • La barra, un intervalo: */15 en los minutos es cada 15 minutos, y 0 */6 * * *, cada seis horas en punto.
  • Hay atajos que reemplazan los cinco campos: @hourly, @daily, @weekly, @monthly y @reboot, que corre una vez cuando arranca cron.
  • Una trampa: si ponés algo en el día del mes y también en el día de la semana, cron corre cuando se cumple cualquiera de los dos. 0 6 1 * 5 corre el día 1 de cada mes y, además, todos los viernes.

Horarios que se usan seguido

ExpresiónCorre
*/10 * * * *Cada 10 minutos
0 */2 * * *Cada dos horas, en punto
15 2 * * *Todos los días a las 2:15
0 8 * * 1Los lunes a las 8:00
30 22 * * 5Los viernes a las 22:30
0 5 1,15 * *Los días 1 y 15 de cada mes a las 5:00
0 0 1 1 *Una vez por año, al empezar el 1 de enero

Cron usa el reloj y la zona horaria del servidor, y no sabe de feriados.

Cargar, ver y editar las tareas

Cada usuario tiene su propio crontab, y cada tarea corre con los permisos de ese usuario. Lo que necesita root va en el crontab de root; lo demás, mejor en el de un usuario común.

crontab -e              # abre tu crontab en el editor
crontab -l              # lista las tareas cargadas
sudo crontab -u ana -e  # edita el crontab de otro usuario
  • La primera vez, Debian y Ubuntu preguntan qué editor usar; nano es el más amable. AlmaLinux abre vi: si preferís nano, corré EDITOR=nano crontab -e (y si no está, dnf install nano).
  • Al guardar y salir, la tarea queda activa. No hay que reiniciar nada.
  • Si crontab no existe, falta el paquete: apt install cron en Debian y Ubuntu; dnf install cronie y después systemctl enable --now crond en AlmaLinux.
  • Además del crontab de cada usuario están los archivos de /etc/cron.d/, que llevan un campo más, el usuario, entre el horario y el comando: 15 2 * * * root /opt/scripts/limpieza.sh.
  • Las carpetas /etc/cron.daily/, /etc/cron.weekly/ y similares corren una vez por período todo lo que tengan adentro. En Debian y Ubuntu esos archivos no pueden tener punto en el nombre: un limpieza.sh ahí se ignora sin aviso; llamalo limpieza.

La zona horaria, antes de la primera tarea

Si el servidor está en UTC y vos pensás en hora argentina, tus tareas corren tres horas antes de lo que creés. Fijate con timedatectl y, si hace falta, cambiala. Cron toma la zona al arrancar, así que reinicialo después:

timedatectl set-timezone America/Argentina/Buenos_Aires
systemctl restart cron     # Debian y Ubuntu
systemctl restart crond    # AlmaLinux

Por qué anda a mano y en cron no

Casi siempre es el entorno. Una tarea de cron no pasa por tu .bashrc ni por tu perfil: arranca con unas pocas variables y parada en tu carpeta personal. Para ver exactamente con qué corre, cargá por un minuto esta línea, mirá el archivo que genera y borrala:

* * * * * env > /tmp/entorno-cron.txt
  • El PATH es mínimo y no incluye /usr/local/bin. Usá rutas completas (command -v php te dice cuál) o definí al principio del crontab PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin.
  • El intérprete es /bin/sh, que en Debian y Ubuntu es dash y no bash. Si la línea usa cosas propias de bash, pasala a un script que empiece con #!/bin/bash o poné SHELL=/bin/bash arriba de todo.
  • El directorio de trabajo es tu carpeta personal. Si el script abre archivos con rutas relativas, entrá primero a su carpeta: cd /opt/scripts && ./informe.sh.
  • Lo que imprime la tarea se manda por correo al dueño del crontab, o a la dirección de MAILTO, solo si el servidor tiene un programa de envío instalado. Si no lo tiene, los errores se pierden. Redirigí siempre la salida: >> /var/log/informe.log 2>&1.

Que una ejecución no se pise con la siguiente

Si una tarea que corre cada 10 minutos a veces tarda 15, vas a tener dos copias a la vez peleándose por los mismos archivos. flock lo evita: toma un candado y, con -n, si la anterior sigue ocupada, la nueva se saltea.

*/10 * * * * /usr/bin/flock -n /tmp/sincronizar.lock /opt/scripts/sincronizar.sh >> /var/log/sincronizar.log 2>&1

Timers de systemd: cron con registro y con memoria

Un timer de systemd hace lo mismo que una línea de cron, con tres ventajas: lo que imprime la tarea queda en el registro del sistema sin configurar nada; con Persistent=true, corre al arrancar si el servidor estaba apagado a la hora prevista; y nunca lanza una segunda copia mientras la primera sigue activa. Son dos archivos con el mismo nombre. El servicio dice qué correr:

# /etc/systemd/system/limpieza.service
[Unit]
Description=Borra temporales de más de 7 días
[Service]
Type=oneshot
ExecStart=/usr/bin/find /var/tmp/miapp -type f -mtime +7 -delete

Y el timer, cuándo:

# /etc/systemd/system/limpieza.timer
[Unit]
Description=Limpieza diaria
[Timer]
OnCalendar=*-*-* 02:15:00
Persistent=true
RandomizedDelaySec=5min
[Install]
WantedBy=timers.target
  1. Cargá las unidades nuevas con systemctl daemon-reload.
  2. Activá el timer, no el servicio: systemctl enable --now limpieza.timer.
  3. Para probar la tarea sin esperar a la hora, corré el servicio a mano: systemctl start limpieza.service.
  4. systemctl list-timers muestra cuándo toca la próxima ejecución y cuándo fue la última; journalctl -u limpieza.service, lo que imprimió.

Cómo se escriben los horarios en OnCalendar

Antes de usar una expresión, probala: systemd-analyze calendar "Mon..Fri 07:45" te dice cómo la interpreta y cuándo sería la próxima vez.

OnCalendarEquivale a
hourlyCada hora en punto
dailyTodos los días a la medianoche
Mon..Fri 07:45Días hábiles a las 7:45
*-*-01 05:00El día 1 de cada mes a las 5:00
*-*~01 23:55El último día de cada mes a las 23:55, que en cron pide un truco
*:0/10Cada 10 minutos

Un script que corra cada vez que arranca el servidor

@reboot en cron sirve para algo simple, pero no espera a que la red esté lista y no te deja ver cómo terminó. Para lo que importa, usá una unidad de systemd. Si en lugar de un script que termina es un programa que queda corriendo, las opciones están en dejar un proceso corriendo en el VPS.

# /etc/systemd/system/al-arrancar.service
[Unit]
Description=Tareas al iniciar el servidor
Wants=network-online.target
After=network-online.target
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/opt/scripts/al-arrancar.sh
[Install]
WantedBy=multi-user.target
  • El script necesita permiso de ejecución (chmod +x /opt/scripts/al-arrancar.sh) y una primera línea como #!/bin/bash.
  • Activala con systemctl daemon-reload y systemctl enable al-arrancar.service. Con --now, además, corre en ese momento.
  • RemainAfterExit=yes hace que figure como activa después de terminar, así systemctl status al-arrancar.service te confirma que ya corrió en este arranque.
  • En AlmaLinux, si el script llegó a su carpeta con mv desde otra, corré restorecon -v /opt/scripts/al-arrancar.sh: puede conservar una etiqueta de SELinux que le impide a systemd ejecutarlo.

En el hosting, las tareas se cargan desde DirectAdmin

En un plan de hosting no tenés root ni crontab por terminal, pero DirectAdmin tiene su pantalla de tareas cron con los mismos cinco campos y el comando. Corren con tu usuario y valen las mismas reglas: rutas completas, \% y la salida a un archivo de tu cuenta. El paso a paso, con el ejemplo del cron de WordPress, está en tareas programadas en DirectAdmin.

Preguntas frecuentes

¿Dónde veo si cron ejecutó mi tarea?

Cron anota cada ejecución en el registro del sistema: journalctl -u cron en Debian y Ubuntu, journalctl -u crond en AlmaLinux, que además la deja en /var/log/cron. Eso confirma que arrancó; si terminó bien lo dice el archivo al que mandaste la salida.

¿Cómo corro algo el último día de cada mes con cron?

Cron no tiene una forma directa. El truco es programarlo del 28 al 31 y dejar que siga solo si mañana es día 1: 55 23 28-31 * * [ "$(date -d tomorrow +\%d)" = 01 ] && /opt/scripts/cierre.sh. Con un timer de systemd alcanza con OnCalendar=*-*~01 23:55.

¿Cron o timer de systemd?

Para una tarea simple en un servidor que está siempre prendido, cron se escribe más rápido y lo entiende cualquiera. El timer conviene cuando no podés perder una ejecución si el equipo estuvo apagado, cuando querés el historial en el registro del sistema o cuando la tarea depende de otro servicio.

¿Puedo editar el archivo del crontab directamente?

Mejor no. Los crontabs viven en /var/spool/cron/, pero crontab -e revisa la sintaxis al guardar y se asegura de que cron tome el cambio. Editado a mano, un error de sintaxis puede hacer que cron ignore el archivo entero y deje de correr todas las tareas de ese usuario.

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