Programando y depurando tareas con Cron
Publicado el Lunes, 27 de julio de 2026cron es una de las tantas herramientas geniales de sistemas Unix. Nos permite programar tareas para que se ejecuten automáticamente en momentos determinados. Podemos programar comandos que se ejecuten a una hora determinada en el día, un día específico del mes, etcétera. cron tiene reputación por ser "complicado", pero no se lo merece. Como otras tantas aplicaciones, es cuestión de tomarse el tiempo de leer y aprender cómo usarlas.
Está disponible probablemente en todas las distribuciones Linux. Pero no siempre viene instalado por defecto. Algunas distribuciones usan systemd timers, que tengo entendido cumplen la misma función. Y seguramente haya alguna otra implementación nueva que no conozco.

Recientemente tuve que usar cron para programar la actualización de mi sitio web personal. El sitio usa Middleman para generar archivos estáticos dinámicamente. Solía tenerlo alojado en GitHub, generándose automáticamente mediante GitHub Actions. Pero al moverlo a un servidor de alojamiento web común, tenía pendiente la actualización automática. La experiencia me sirvió para aprender más sobre cron y comparto mi experiencia con la esperanza de que le pueda servir a alguien más también.
Dependiendo de cada sistema, los directorios y ubicación de distintos archivos puede llegar a variar. Esta entrada está basada en mi experiencia con Debian GNU/Linux 12 (bookworm) en mi Raspberry Pi. En mi caso, no tuve que instalar nada, ya que cron venía instalado por defecto. Sin caer en el comportamiento poco productivo de "RTFM", tengo que señalar que man cron es un recurso muy bueno para aprender a usar cron. No hay que tenerle miedo a las man pages, que man es por manual, no por hombre. Realmente ayudan a comprender mejor estas cosas.
cron se inicia automáticamente desde /etc/init.d al entrar en niveles de ejecución multi-usuario. Las tareas para cron se definen en archivos crontab (tablas cron). Hay tablas cron a nivel sistema, pero también tenemos archivos crontab específicos de nuestro usuario. cron se despierta cada minuto, examina todos sus archivos crontab y cada comando para ver si tiene que ejecutar algo en el minuto actual.
El archivo /etc/crontab es la tabla cron a nivel sistema. En este se especifican tareas y el usuario que debe ejecutarlas:
# /etc/crontab: system-wide crontab
# Unlike any other crontab you don't have to run the `crontab'
# command to install the new version when you edit this file
# and files in /etc/cron.d. These files also have username fields,
# that none of the other crontabs do.
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
# Example of job definition:
# .---------------- minute (0 - 59)
# | .------------- hour (0 - 23)
# | | .---------- day of month (1 - 31)
# | | | .------- month (1 - 12) OR jan,feb,mar,apr ...
# | | | | .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
# | | | | |
# * * * * * user-name command to be executed
17 * * * * root cd / && run-parts --report /etc/cron.hourly
25 6 * * * root test -x /usr/sbin/anacron || { cd / && run-parts --report /etc/cron.daily; }
47 6 * * 7 root test -x /usr/sbin/anacron || { cd / && run-parts --report /etc/cron.weekly; }
52 6 1 * * root test -x /usr/sbin/anacron || { cd / && run-parts --report /etc/cron.monthly; }
Este archivo tiene algunas cosas específicas de Debian. cron tiene soporte y está predefinido para también ejecutar tareas en los crontabs hourly, daily, weekly y monthly, (cada hora, diario, semanal, mensual). Importante ver que las variables SHELL y PATH están definidas en el crontab, algo a lo que voy a volver más adelante.
cron también revisa el directorio /var/spool/cron/crontabs, que contiene los archivos crontab nombrados a partir de las cuentas de usuario en /etc/passwd. Por ejemplo:
fernando
Estos archivos se cargan en memoria, y no deberían ser accedidos directamente. Para acceder a ellos y actualizarlos debemos usar el comando crontab. El formato para definir una tarea cron es específico y está bastante bien documentado en los crontabs. Si ejecutamos crontab -l, podemos ver el archivo de tabla cron actual de nuestro usuario en el sistema. Para editarlo, ejeuctamos crontab -e.
Definiendo tareas para cron
La primera parte de una definición de tarea cron es la periodicidad. Son 5 campos que definen el minuto, hora, día, mes y día de la semana. Podemos usar asteriscos para definir "todos" y guiones para definir un rango. Por ejemplo el formato * * * * * dice "quiero ejecutar esta tarea cada minuto". El formato 0 6 * * * dice "quiero ejecutar esta tarea todos los días a las 6:00". Hay varias herramientas en línea para verificar la peroidicidad como crontab.guru y crontab.io.
Los guiones sirven por ejemplo para ejecutar una tarea sólo de lunes a viernes: 0 6 * * 1-5. O una tarea a ejecutar el primer día de cada mes a las 3 de la tarde: 0 15 1 * *.
La segunda parte de la definición de una tarea es el comando a ejecutar. Puede ser un comando directo o un script. Para cosas sencillas se puede ejecutar el comando directamente. Si por ejemplo ingreso esto en mi crontab:
Al minuto siguiente me voy a encontrar el archivo hola.txt en mi home con el texto "Hola mundo".
Ejemplo real: Depurando problemas con cron
Para el ejemplo real en el que quise usar cron, escribí un script para después llamar desde mi crontab. En la Raspberry Pi tengo un montón de cosas ejecutables en el directorio /home/fernando/bin/. Así que creé un directorio nuevo fb_site y ahí hice check out del código fuente del sitio. También ahí mismo cree el script publish.sh que ejecuta los pasos para generar el código nuevo y publicarlo (subirlo por FTP a un servidor):
cd /home/fernando/bin/fb_site && # Entro al directorio
source .env && # Cargo las variables de .env
cd /home/fernando/bin/fb_site/fb_site/ && # Entro al directorio con el código fuente del sitio
bundle exec middleman build && # middleman genera los archivos html, css y js
bundle exec middleman deploy # Publica los cambios por FTP
Como describo en el script: Entro al directorio necesario, cargo el archivo .env al shell para tener disponibles los datos para subir los archivos a FTP, voy al directorio del código y ejecuto los comandos middleman para generar el código en el directorio build y los publico por FTP.
El siguiente paso fue agregar el script a mi crontab personal con crontab -e. Quiero que el sitio se actualice todos los días a las 6 de la mañana, así que agregué al final del archivo:
Para probar, empecé cambiando la hora a "dentro de 1 minuto", para ver si el sitio se actualizaba.
Inicialmente el script no estaba funcionando. Así que empecé a pensar formas de depurar y encontrar la razón. Lo primero que podemos hacer es comprobar que el script se está ejecutando. Por ejemplo, agregando una línea que escribe a un archivo de log: echo "`date` Publicando fernandobriano.com" >> /home/fernando/publish.log. Volvemos a editar el crontab para que se ejecute en el próximo minuto, y comprobamos que se creó el archivo publish.log.
El script se estaba ejecutando correctamente, pero por alguna razón, las partes con Ruby no estaban funcionando. Fue ahí donde me sirvió lo que comentaba más arriba de redefinir PATH en el crontab. El momento "Eureka" (o "me cayó la ficha" más en uruguayo) fue cuando razoné que seguramente cron ejecutara un shell mucho más liviano que mi shell personal donde venía probando el script. Una prueba que podemos hacer en ese momento, es escribir a un log la salida del comando env, donde vemos todas las variables definidas para el usuario/shell en ejecución.
La ejecución de Ruby
Mi script necesita bundler para correr middleman, una gema Ruby. Estoy usando rbenv, como comenté por acá, para gestionar versiones de Ruby. Estas herramientas agregan valores al PATH para poder usar distintas versiones de Ruby en nuestro sistema. Una de las pruebas que me confirmó que iba por buen camino fue cuando imprimí el valor de which ruby en un archivo desde cron, y me mostró el Ruby instalado por el sistema (no Ruby 4.x, como venía usando con mi usuario).
Así que bajo mi usuario, ejecuté env y me fijé qué agregaba rbenv a PATH:
PATH=(...):/home/fernando/.rbenv/shims:(...)
Con esta información, modifiqué el script publish.sh para que también agregue este directorio al path:
#!/bin/bash
echo "`date` Publicando fernandobriano.com" >> /home/fernando/publish.log
PATH=$PATH:/home/fernando/.rbenv/shims &&
cd /home/fernando/bin/fb_site &&
source .env &&
cd /home/fernando/bin/fb_site/fb_site/ &&
bundle exec middleman build &&
bundle exec middleman deploy
echo "`date` Publicación finalizada" >> /home/fernando/publish.log
Y listo, quedó funcionando y mi sitio fernandobriano.com se actualiza automáticamente todos los días. Espero que este post haya ayudado a perderle el miedo a cron y la experiencia de depuración le sirva a alguien más.








Arlequín 27 julio. 2026 - 16:09
Qué bueno este post. `cron` es lo más grande que hay. El archivo crontab que te trae Debian 12 tiene el párrafo de comentarios perfecto para no perserse nunca. Yo suelo ir siempre al manual: `man 5 crontab` para recordar el orden. Y, se paso, uso el man, que para algo está escrito y, a veces, traducido.
¿Conocés `at`? Es genial, también. En los Unices suele venir instalado por defecto; no así en Linux. Tiene su propio daemon.
Podés hacer cosas como programar una tarea para las 5 PM: `at teatime ~/scripts/runme_once.sh`, por ejemplo.
Fernando 27 julio. 2026 - 18:51
¡Gracias! Sí, con los man pages o mismo ese comentario del crontab, es la única referencia necesaria. Generalmente son súper completos, particularmente en distribuciones como Debian o ArchLinux. Antes de lo que comento en el post, hacía años que no usaba cron, no me acordaba de nada. Y con la documentación del sistema mismo ya me alcanzó para volver a aprender y dejarlo andando.
No conocía
at, tenía idea que había algo tipocronpara programar tareas así.Hablando de Unices, ando con ganas de ponerme a aprender BSD, por aprender algo distinto…