Crontab en Linux: cómo programar tareas automáticamente

Un backup que debería correr todos los días a las 3 de la madrugada, pero solo corre cuando alguien se acuerda de ejecutar el script a mano. Un log que crece sin rotación hasta tumbar el disco. Un certificado que vence porque nadie automatizó la renovación. Todos estos problemas tienen la misma causa: una tarea que debería ser automática sigue dependiendo de que un humano se acuerde. El crontab existe desde los primeros Unix justamente para esto — y la mayoría de los sysadmins usan solo una fracción de lo que ofrece.
Qué es cron y para qué sirve
cron es un daemon que corre en segundo plano en prácticamente toda distribución Linux y va comprobando, minuto a minuto, si alguna programación coincide con la hora actual. El crontab ("cron table") es el archivo de texto donde listas esas programaciones — una por línea, cada una indicando cuándo ejecutar y qué ejecutar. No hace falta instalar nada: cron y crontab ya vienen en Debian, Ubuntu, RHEL, Alpine y prácticamente cualquier imagen de servidor.
Para editar el crontab del usuario actual:
crontab -ePara listar lo que ya está programado (hazlo antes de cualquier cambio — adivinar qué está corriendo es como las cosas se rompen de madrugada):
crontab -lLa sintaxis de los 5 campos
Toda línea del crontab sigue el mismo formato: cinco campos de tiempo, seguidos del comando.
# ┌───────────── minuto (0-59)
# │ ┌───────────── hora (0-23)
# │ │ ┌───────────── día del mes (1-31)
# │ │ │ ┌───────────── mes (1-12)
# │ │ │ │ ┌───────────── día de la semana (0-6, 0 = domingo)
# │ │ │ │ │
* * * * * comando-a-ejecutarCuatro caracteres especiales cubren el 90% de los casos:
- Asterisco (
*): "cualquier valor".* * * * *se ejecuta cada minuto. - Barra (
/): define un intervalo.*/15en el campo de minuto se ejecuta cada 15 minutos. - Coma (
,): lista de valores.1,15en el campo de día se ejecuta el día 1 y el día 15. - Guion (
-): intervalo.1-5en el campo de día de la semana cubre de lunes a viernes.
¿Cómo ejecutar un script todos los días a las 3 de la madrugada?
Usa 0 3 * * * seguido de la ruta completa del script. El cero en el campo de minuto y el 3 en el campo de hora fijan el horario; los tres asteriscos restantes indican "todos los días, todos los meses, cualquier día de la semana".
0 3 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1Ejemplos prácticos para copiar y adaptar
# Backup diario a las 3h, log de salida y error en el mismo archivo
0 3 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1
# Limpieza de logs cada domingo a las 4h
0 4 * * 0 find /var/log/app -name "*.log" -mtime +30 -delete
# Renovación de certificado Let's Encrypt, dos veces al día (recomendación oficial)
0 0,12 * * * certbot renew --quiet
# Sincronización cada 15 minutos, solo en días de semana
*/15 * * * 1-5 /usr/local/bin/sync-dados.sh
# Reiniciar un servicio el primer día de cada mes a medianoche
0 0 1 * * systemctl restart app-staleTambién existen atajos para no escribir los 5 campos a mano: @reboot (una vez, al arrancar el sistema), @daily (equivale a 0 0 * * *), @weekly, @monthly y @hourly. Son útiles para programaciones obvias donde la legibilidad importa más que la precisión.
Los 3 errores que más rompen los cron job en producción
Funciona perfecto cuando pruebas el script directamente en la terminal, y falla silenciosamente cuando lo dispara cron. Casi siempre es uno de estos tres problemas:
- PATH incompleto: cron corre con un entorno mínimo, sin el
PATHde tu shell interactivo. Un script que llama apython,nodeoawssin ruta completa falla con "comando no encontrado". Solución: usa rutas absolutas (/usr/bin/python3) o definePATH=al inicio del crontab. - Sin redirección de salida: por defecto cron intenta enviar stdout/stderr por correo local — que en la mayoría de los servidores no está configurado, así que la salida simplemente desaparece. Redirige siempre con
>> archivo.log 2>&1para tener rastro de lo que ocurrió. - Las variables de entorno del shell no se cargan:
.bashrc,.profiley las variables exportadas en la sesión interactiva no existen en el entorno de cron. Si el script depende de una variable, expórtala explícitamente dentro del propio script o en el crontab.
Cuándo cambiar crontab por systemd timer
¿Crontab o systemd timer: cuál usar?
Para programaciones simples en cualquier Linux, cron sigue siendo suficiente. Para jobs críticos que necesitan log centralizado, dependencias entre servicios o recuperación de una ejecución perdida, systemd timer es la opción más robusta en las distros que ya corren systemd (Ubuntu, Debian, RHEL, Fedora).
El systemd timer separa la programación en dos archivos: una unit .service (qué ejecutar) y una unit .timer (cuándo ejecutar). Ejemplo equivalente al backup de las 3h:
# /etc/systemd/system/backup.service
[Unit]
Description=Backup diario
[Service]
ExecStart=/usr/local/bin/backup.sh
# /etc/systemd/system/backup.timer
[Unit]
Description=Programación del backup diario
[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true
[Install]
WantedBy=timers.targetsudo systemctl enable --now backup.timerLa ventaja práctica de Persistent=true: si el servidor estaba apagado a las 3h, el timer dispara el job en cuanto systemd vuelve a correr — algo que el cron tradicional no hace. Los logs también quedan centralizados en el journal (journalctl -u backup.service), sin depender de redirección manual. A cambio, da más trabajo escribirlo (dos archivos en vez de una línea) — por eso cron todavía gana en scripts puntuales y entornos más simples.
Seguridad: cron como root es poder de root
Un job programado con crontab -e dentro de una sesión root, o colocado directamente en /etc/cron.d/, se ejecuta con privilegio total — sin prompt de contraseña, sin registro de quién ejecutó el comando manualmente. Es un vector clásico de persistencia después de un compromiso inicial: un atacante con acceso de escritura en cualquier directorio de cron puede ejecutar código periódicamente sin necesidad de mantener una sesión abierta. Si ya aplicaste los cambios de hardening de SSH en el servidor, vale el mismo cuidado aquí: audita crontab -l como root periódicamente, restringe quién puede editar cron vía /etc/cron.allow, y nunca ejecutes como root un job que podría correr con un usuario de servicio dedicado.
El equivalente en el mundo Windows es el Programador de tareas, y el principio de auditoría es el mismo: si administras un entorno mixto, el artículo sobre cómo auditar los registros de seguridad de Windows con PowerShell cubre el otro extremo de esa rutina. Y si tu entorno Windows todavía depende de Windows PowerShell 5.1 para ejecutar esos scripts programados, ya documentamos qué cambia en la migración a PowerShell 7.6 LTS.
Cron no es glamuroso, pero es probablemente la herramienta que más silenciosamente sostiene la infraestructura en producción — backup, rotación de logs, renovación de certificados, sincronización de datos. Audita lo que está corriendo en tu servidor ahora mismo con crontab -l y ls /etc/cron.d/: si encuentras algo que nadie recuerda haber programado, es hora de investigar antes de asumir que es solo una tarea olvidada.




Comentarios