Programar tareas en Linux con cron y con timers de systemd
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ón | Campo | Valores | En el ejemplo |
|---|---|---|---|
| 1 | Minuto | 0 a 59 | 45 |
| 2 | Hora | 0 a 23 | 7 |
| 3 | Día del mes | 1 a 31 | *, cualquiera |
| 4 | Mes | 1 a 12, o jan a dec | *, cualquiera |
| 5 | Día de la semana | 0 a 7; el 0 y el 7 son domingo | 1-5, de lunes a viernes |
| 6 | Comando | Con 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-5en el día de la semana es de lunes a viernes. - La barra, un intervalo:
*/15en los minutos es cada 15 minutos, y0 */6 * * *, cada seis horas en punto. - Hay atajos que reemplazan los cinco campos:
@hourly,@daily,@weekly,@monthlyy@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 * 5corre el día 1 de cada mes y, además, todos los viernes.
Horarios que se usan seguido
| Expresión | Corre |
|---|---|
*/10 * * * * | Cada 10 minutos |
0 */2 * * * | Cada dos horas, en punto |
15 2 * * * | Todos los días a las 2:15 |
0 8 * * 1 | Los lunes a las 8:00 |
30 22 * * 5 | Los 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
crontabno existe, falta el paquete:apt install cronen Debian y Ubuntu;dnf install croniey despuéssystemctl enable --now cronden 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: unlimpieza.shahí se ignora sin aviso; llamalolimpieza.
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 # AlmaLinuxPor 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 phpte dice cuál) o definí al principio del crontabPATH=/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/basho ponéSHELL=/bin/basharriba 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>&1Timers 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 -deleteY 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- Cargá las unidades nuevas con
systemctl daemon-reload. - Activá el timer, no el servicio:
systemctl enable --now limpieza.timer. - Para probar la tarea sin esperar a la hora, corré el servicio a mano:
systemctl start limpieza.service. systemctl list-timersmuestra 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.
| OnCalendar | Equivale a |
|---|---|
hourly | Cada hora en punto |
daily | Todos los días a la medianoche |
Mon..Fri 07:45 | Días hábiles a las 7:45 |
*-*-01 05:00 | El día 1 de cada mes a las 5:00 |
*-*~01 23:55 | El último día de cada mes a las 23:55, que en cron pide un truco |
*:0/10 | Cada 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-reloadysystemctl enable al-arrancar.service. Con--now, además, corre en ese momento. RemainAfterExit=yeshace que figure como activa después de terminar, asísystemctl status al-arrancar.servicete confirma que ya corrió en este arranque.- En AlmaLinux, si el script llegó a su carpeta con
mvdesde 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
Cron en DirectAdmin: cómo programar tareas en tu hosting
Cómo crear una tarea cron en DirectAdmin, leer los cinco campos del horario, elegir entre PHP por consola o URL y evitar el correo que llega en cada corrida.
8 min de lectura
Cómo dejar un proceso corriendo en el VPS después de cerrar SSH
tmux para trabajos largos, systemd-run para tareas de una sola vez y una unidad de systemd para lo que tiene que arrancar solo, con los errores más comunes.
7 min de lectura
Usuario administrador con sudo: crearlo y dejar de entrar como root
Cómo crear tu usuario con sudo en Debian, Ubuntu y AlmaLinux, pasarle la clave SSH, probarlo y recién después cerrar el acceso de root por SSH.
7 min de lectura
