Cron es el planificador clásico de Unix: un demonio que cada minuto revisa las tablas de tareas (crontabs) y ejecuta los comandos cuya hora coincide. Sigue siendo la forma más sencilla de lanzar copias de seguridad, limpiezas o sincronizaciones periódicas. En este tutorial aprenderás la sintaxis de cron y programarás tareas en Ubuntu 24.04, tanto de usuario como de sistema, controlando su entorno, su salida y los solapamientos.

Requisitos previos

Para seguir esta guía necesitas:

  • Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath. En Debian 12 todo funciona igual.
  • Un usuario no root con privilegios sudo.

Paso 1: Comprobar que cron está activo

Ubuntu instala y arranca cron por defecto. Comprueba el estado del servicio:

systemctl status cron
● cron.service - Regular background program processing daemon
     Loaded: loaded (/usr/lib/systemd/system/cron.service; enabled; preset: enabled)
     Active: active (running) since Thu 2026-09-25 09:12:44 UTC; 1h 3min ago

Si no aparece instalado (algunas imágenes mínimas lo omiten), instálalo y actívalo:

sudo apt install cron
sudo systemctl enable --now cron

Cron usa la zona horaria del sistema. Compruébala antes de programar nada, porque 0 3 * * * significa las 03:00 de esa zona:

timedatectl | grep "Time zone"
                Time zone: Etc/UTC (UTC, +0000)

Si la cambias con sudo timedatectl set-timezone Europe/Madrid, reinicia cron después con sudo systemctl restart cron para que la aplique.

Paso 2: Entender la sintaxis de cron

Cada línea de un crontab tiene cinco campos de tiempo seguidos del comando:

minuto  hora  día_del_mes  mes  día_de_la_semana  comando
PosiciónCampoValores permitidos
1Minuto0-59
2Hora0-23
3Día del mes1-31
4Mes1-12 o jan-dec
5Día de la semana0-7 (0 y 7 son domingo) o sun-sat

Dentro de cada campo puedes usar:

  • *: cualquier valor.
  • ,: lista de valores, como 1,15.
  • -: rango, como 1-5.
  • /: intervalo, como */10 (cada 10) o 9-17/2 (de 9 a 17 cada 2).

Algunos ejemplos habituales:

ExpresiónCuándo se ejecuta
*/5 * * * *Cada 5 minutos
0 * * * *Al principio de cada hora
30 3 * * *Todos los días a las 03:30
0 9 * * 1Los lunes a las 09:00
0 18 * * 1-5De lunes a viernes a las 18:00
*/15 9-17 * * 1-5Cada 15 minutos en horario laboral
0 0 1 * *El día 1 de cada mes a medianoche
0 6,18 * * *A las 06:00 y a las 18:00

También existen atajos que sustituyen a los cinco campos: @hourly, @daily, @weekly, @monthly, @yearly y @reboot (una vez al arrancar cron).

Hay dos reglas que causan muchos errores:

  • Si indicas a la vez día del mes y día de la semana, cron ejecuta la tarea cuando se cumple cualquiera de los dos, no ambos. 0 0 13 * 5 se ejecuta todos los días 13 y todos los viernes.
  • El carácter % en el comando se convierte en un salto de línea. Para usarlo, por ejemplo en date +%F, escríbelo como \%.

Paso 3: Crear tu primera tarea con crontab

Cada usuario tiene su propio crontab, que se edita con crontab -e. La primera vez te pedirá que elijas un editor; nano es la opción más sencilla:

crontab -e

Añade al final del archivo una tarea de prueba que escriba la fecha cada minuto. Sustituye your_user por tu nombre de usuario:

* * * * * /usr/bin/date >> /home/your_user/cron-test.log 2>&1

Guarda y cierra. Cron verifica la sintaxis al guardar y muestra crontab: installing new crontab. Lista tu crontab para confirmarlo:

crontab -l | tail -n 1
* * * * * /usr/bin/date >> /home/your_user/cron-test.log 2>&1

Espera un par de minutos y revisa el archivo:

cat ~/cron-test.log
Thu Sep 25 10:21:01 UTC 2026
Thu Sep 25 10:22:01 UTC 2026

Cuando hayas comprobado que funciona, vuelve a crontab -e y borra la línea de prueba. Evita crontab -r: borra todo tu crontab sin pedir confirmación.

Como administrador puedes ver o editar el crontab de otro usuario con sudo crontab -u otro_usuario -l o sudo crontab -u otro_usuario -e.

Paso 4: Controlar el entorno de las tareas

Cron no carga tu .bashrc ni tu perfil. Las tareas se ejecutan con /bin/sh y un PATH mínimo (/usr/bin:/bin), por eso un comando que funciona en la terminal puede fallar en cron con command not found. Hay dos soluciones, y conviene aplicar ambas:

  1. Usar rutas absolutas en los comandos y scripts (/usr/local/bin/mi-script, no mi-script).
  2. Declarar las variables al principio del crontab.

Abre de nuevo tu crontab y añade estas líneas al principio:

SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
MAILTO=""

MAILTO="" desactiva el envío de la salida por correo. Sin un servidor de correo configurado esos mensajes no llegan a ninguna parte, así que es mejor redirigir la salida explícitamente, como verás en el paso 7.

Para reproducir el entorno de cron al probar un script, ejecútalo con un entorno vacío:

env -i HOME="$HOME" PATH=/usr/bin:/bin /bin/sh -c '/usr/local/bin/mi-script'

Si falla así, fallará también en cron.

Paso 5: Crear tareas del sistema en /etc/cron.d

Para tareas que forman parte de la configuración del servidor (y que quieres gestionar como archivos, por ejemplo con Ansible), usa /etc/cron.d. Su formato tiene un campo más: el usuario que ejecuta el comando, entre la hora y el comando.

Crea un archivo para una copia de seguridad diaria:

sudo nano /etc/cron.d/local-backup
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

# Copia de seguridad diaria a las 02:30
30 2 * * * root /usr/local/sbin/local-backup >> /var/log/local-backup.log 2>&1

Ten en cuenta estas reglas, porque si no se cumplen cron ignora el archivo sin avisar:

  • El nombre solo puede contener letras, números, guiones y guiones bajos. Un archivo llamado backup.cron o backup.sh no se ejecuta.
  • El archivo debe pertenecer a root y no ser escribible por otros usuarios.
  • La última línea debe terminar con un salto de línea.

Ajusta los permisos:

sudo chmod 644 /etc/cron.d/local-backup

No hace falta reiniciar cron: detecta los cambios en /etc/cron.d y en los crontabs automáticamente.

Además existen los directorios /etc/cron.hourly, /etc/cron.daily, /etc/cron.weekly y /etc/cron.monthly, donde basta con dejar un script ejecutable (con las mismas reglas de nombre). Para ver qué scripts se ejecutarían en cron.daily:

run-parts --test /etc/cron.daily
/etc/cron.daily/apt-compat
/etc/cron.daily/dpkg
/etc/cron.daily/logrotate

Paso 6: Evitar ejecuciones solapadas con flock

Si una tarea tarda más que su intervalo, cron lanza otra copia aunque la anterior no haya terminado. Dos sincronizaciones escribiendo a la vez en el mismo destino pueden corromper datos. flock, incluido en Ubuntu, resuelve esto con un bloqueo de archivo:

*/10 * * * * /usr/bin/flock -n /tmp/sync.lock /usr/local/bin/sync-data >> /home/your_user/sync.log 2>&1

Con -n, si el bloqueo ya está tomado, flock termina de inmediato sin ejecutar el comando. El bloqueo se libera solo cuando termina el proceso, incluso si falla, así que no quedan archivos de bloqueo huérfanos como ocurre con los scripts que crean un .pid a mano.

Para comprobar el comportamiento, abre dos terminales. En la primera toma el bloqueo durante 30 segundos:

flock -n /tmp/sync.lock sleep 30

En la segunda, intenta tomarlo mientras tanto:

flock -n /tmp/sync.lock echo "ejecutado"; echo "código: $?"
código: 1

El comando no se ha ejecutado porque la primera instancia sigue en marcha.

Paso 7: Registrar la salida y revisar los logs

Por defecto la salida de una tarea se envía por correo al usuario, lo que sin un servidor de correo equivale a perderla. Redirígela siempre:

RedirecciónResultado
>> /ruta/tarea.log 2>&1Añade la salida estándar y los errores a un archivo
2>&1 | /usr/bin/logger -t mi-tareaEnvía todo al journal con la etiqueta mi-tarea
> /dev/null 2>&1Descarta la salida (solo si de verdad no la necesitas)

Enviar la salida al journal evita tener que rotar archivos de log propios. Por ejemplo:

0 4 * * * /usr/local/bin/sync-data 2>&1 | /usr/bin/logger -t sync-data

Y se consulta con:

journalctl -t sync-data --since today

El propio demonio de cron registra cada ejecución en el journal. Para ver las últimas:

journalctl -u cron -n 20 --no-pager
sep 25 10:22:01 servidor CRON[5120]: pam_unix(cron:session): session opened for user your_user(uid=1000) by your_user(uid=0)
sep 25 10:22:01 servidor CRON[5121]: (your_user) CMD (/usr/bin/date >> /home/your_user/cron-test.log 2>&1)
sep 25 10:22:01 servidor CRON[5120]: pam_unix(cron:session): session closed for user your_user

La línea CMD confirma que cron lanzó el comando. Si aparece pero la tarea no hizo lo esperado, el problema está en el comando o en su entorno, no en la programación.

Solución de problemas

La tarea no aparece en journalctl -u cron: cron no la ha lanzado. Revisa la expresión de tiempo, que el archivo de /etc/cron.d cumpla las reglas de nombre y permisos del paso 5, y que la zona horaria sea la que esperas.

command not found o el script funciona a mano pero no en cron: es el PATH reducido. Usa rutas absolutas o define PATH en el crontab, como en el paso 4.

El comando se corta en un %: cron lo interpreta como salto de línea. Escápalo como \% o mueve el comando a un script.

Permission denied: el script no es ejecutable o el usuario de la tarea no puede leer o escribir las rutas que usa. Comprueba con ls -l y ejecuta el script como ese usuario: sudo -u otro_usuario /ruta/script.

Conclusión

Has aprendido la sintaxis de cron, has programado tareas de usuario con crontab -e y de sistema en /etc/cron.d, has fijado su entorno, has evitado solapamientos con flock y sabes dónde mirar cuando una tarea no se ejecuta.

Como siguientes pasos puedes:

  • Probar los temporizadores de systemd para tareas que necesiten dependencias entre servicios o recuperar ejecuciones perdidas con el servidor apagado.
  • Escribir los scripts que ejecuta cron con modo estricto (set -euo pipefail) para que los fallos no pasen desapercibidos.
  • Monitorizar las tareas críticas con un servicio de heartbeat que avise cuando una tarea deja de ejecutarse.