SSD Nodes Learn Hosting plans →
Guías Matt ConnorPor Matt Connor · Actualizado 2026-09-04

Por qué no se ejecuta una tarea de cron

Descubra cinco causas habituales: PATH mínimo, % sin escapar, crontab equivocado, salida enviada por correo y scripts que dependen de su shell.

Por qué no se ejecuta su tarea de cron

Una tarea de cron que «nunca se ejecuta» casi siempre se ha ejecutado. Se ejecutó en un entorno que no es su shell, falló durante el primer segundo y el mensaje terminó en un lugar que no está revisando. Cinco causas explican casi todos los casos: la ruta de búsqueda, el signo de porcentaje, el archivo crontab incorrecto, la salida enviada por correo y un script que espera una sesión de inicio de sesión.

cron es un daemon (un servicio en segundo plano) que lee archivos crontab e inicia comandos según una programación. No lee su .bashrc, no abre un terminal, no inicia un shell de inicio de sesión y no le informa cuando un comando falla. Todas las causas siguientes se derivan de esos cuatro hechos.

Revise las causas en este orden y empiece por la pregunta que subyace a todas ellas: ¿cron se activó? «cron nunca inició la tarea» y «la tarea se inició y terminó» son problemas distintos que no tienen nada en común. Por tanto, responda primero a esa pregunta.

¿Se ejecutó cron?

El daemon tiene un nombre de unidad diferente según la familia de distribuciones. Compruebe ambos y después lea el registro.

systemctl status cron
systemctl status crond
journalctl -u cron --since "2 hours ago"
journalctl -u crond --since "2 hours ago"

Debian y Ubuntu denominan la unidad cron. Fedora, Rocky y Alma la denominan crond. En una máquina determinada sólo existe uno de esos nombres, por lo que es normal que uno de los dos comandos indique que la unidad es desconocida. No es un error.

Lea las entradas que haya escrito su propio sistema. No busque una línea copiada de una guía, porque el texto varía entre las implementaciones de cron y las configuraciones de registro. Sólo debe comprobar dos cosas: si existe una entrada en el minuto indicado por su programación y si esa entrada menciona su comando. Una entrada que menciona su comando significa que cron cumplió su función y que el fallo está dentro del comando. La ausencia total de entradas significa que cron nunca recibió su programación. Esa es la causa 3 que se indica más abajo.

Algunas imágenes envían los mensajes de cron mediante rsyslog a un archivo en lugar del journal. Busque en /var/log un archivo cuyo nombre haga referencia a cron o syslog y lea sus últimas líneas.

ls -l /var/log
sudo tail -n 50 /var/log/syslog

Si no existe ni la unidad ni el registro, es posible que cron no esté instalado. Las imágenes de nube mínimas y los contenedores suelen omitirlo.

dpkg -l cron
rpm -q cronie
sudo apt install cron
sudo dnf install cronie
sudo systemctl enable --now cron

Causa 1: cron no tiene su PATH

El shell interactivo construye PATH a partir de /etc/profile, ~/.profile, ~/.bashrc y de todo lo que esos archivos cargan. Nada de eso se ejecuta para un trabajo de cron. cron inicia el comando con su propio entorno reducido, por lo que no encuentra un programa ubicado fuera de los directorios estándar del sistema. Cualquier elemento de /usr/local/bin, /opt, un gestor de versiones de lenguajes, un entorno virtual de Python o un espacio de trabajo de Go puede ser la causa. El trabajo falla en su primera línea y el shell escribe un error del tipo "not found". El texto exacto depende del shell que lo haya ejecutado.

Busque la ruta real de cada comando que use el trabajo.

command -v docker
command -v node
readlink -f "$(command -v node)"

Después, escriba esas rutas absolutas en el trabajo o establezca PATH una sola vez al principio del crontab.

PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
0 3 * * * /usr/local/bin/mytool run

Obtenga esa lista en su propia máquina con echo "$PATH" y elimine todo lo que exista sólo dentro de una sesión interactiva. Aquí hay una regla importante: cron no expande variables en estas líneas de asignación. PATH=$PATH:/usr/local/bin almacena el texto literal $PATH:/usr/local/bin, por lo que el trabajo termina con una ruta de búsqueda que no contiene ningún directorio utilizable. Escriba la lista completa.

Un gestor de versiones necesita algo más que una ruta. nvm, pyenv, rbenv y asdf instalan una función de shell o un directorio de shims desde .bashrc, y un trabajo de cron nunca lee ese archivo. Llame al binario versionado mediante su ruta absoluta o cargue el script de inicialización del gestor como primera línea de su propio script.

Causa 2: el signo de porcentaje termina el comando

En el campo de comando de un crontab, % no es un carácter normal. El primer % sin escapar termina el comando. Todo lo que aparece después se entrega al comando como entrada estándar, y cada % posterior se convierte en un salto de línea. Esta es una función real de cron para proporcionar entradas breves a un programa. También explica por qué un nombre de archivo con fecha es la entrada de crontab que suele fallar.

Escriba 0 3 * * * /usr/bin/tar -czf /srv/backups/site-$(date +%F).tar.gz /srv/site y tar nunca verá una fecha formateada. cron corta la línea en el primer %, por lo que el shell recibe una sustitución de comandos sin terminar y el resto de la línea llega como entrada estándar. Escape cada signo de porcentaje con una barra inversa.

0 3 * * * /usr/bin/tar -czf /srv/backups/site-$(date +\%F).tar.gz /srv/site

Dos capas leen esa línea, en este orden. \% es una regla de cron que cron aplica antes de iniciar cualquier proceso. $(date +\%F) es una sustitución de comandos que aplica después el shell iniciado por cron. La clave es saber qué capa interpreta cada carácter.

La práctica más segura consiste en mantener la lógica fuera del crontab. Colóquela en un script, donde el signo de porcentaje no tiene un significado especial.

#!/bin/bash
set -euo pipefail
stamp="$(date +%F)"
tar -czf "/srv/backups/site-${stamp}.tar.gz" /srv/site

La línea del crontab contiene entonces una ruta y una redirección, y nada más. Un crontab que se puede leer de un vistazo también se puede depurar.

Causa 3: ¿qué crontab editó?

No existe un único crontab. Hay varios archivos, con distintos propietarios y distinto número de campos, y un trabajo escrito en el archivo equivocado es invisible.

  • crontab -e edita el crontab del usuario que ejecuta el comando. sudo crontab -e edita el de root. Dos personas que depuran el mismo equipo suelen terminar leyendo dos archivos distintos.
  • sudo crontab -l -u deploy muestra el crontab de otro usuario. Es la forma de confirmar qué está instalado realmente para la cuenta que debe ejecutar el trabajo.
  • /etc/crontab y todos los archivos de /etc/cron.d contienen un campo adicional entre la programación y el comando: el usuario con el que se ejecutará. Si pega una línea de cinco campos de un crontab de usuario en /etc/cron.d, la primera palabra del comando se interpreta como un nombre de usuario.
  • Los archivos de /etc/cron.d deben tener nombres compuestos por letras, dígitos, guiones bajos y guiones. Un archivo llamado backup.sh o site.conf se omite sólo por su nombre. Cámbiele el nombre a backup y vuelva a revisar el registro.
  • Los archivos de /etc/cron.d deben pertenecer a root y no deben tener permisos de escritura para el grupo ni para otros usuarios. ls -l /etc/cron.d muestra ambos datos a la vez.
  • Los scripts colocados en /etc/cron.daily y sus directorios equivalentes siguen la misma regla de nombres y también deben tener el bit de ejecución. Si falta el bit de ejecución, se omiten silenciosamente.
  • /etc/cron.allow y /etc/cron.deny determinan quién puede instalar un crontab. Si alguno existe en el equipo, léalo antes de asumir que su usuario tiene permiso para instalarlo.

Instale un crontab de usuario con el comando crontab en lugar de editar manualmente el archivo de spool, porque crontab analiza el archivo antes de instalarlo. Al guardar, lea lo que el comando muestra. Si rechaza el archivo, la versión anterior sigue activa y el cambio nunca se aplica. Esto parece exactamente que cron le ignora.

El propietario también determina los permisos. Un trabajo del crontab de root crea archivos propiedad de root que la aplicación que los lee quizá no pueda modificar. Un trabajo del crontab de un usuario normal no puede leer un directorio exclusivo de root. Asigne el propietario según el trabajo: el mantenimiento de una aplicación corresponde a la cuenta de esa aplicación. Ese es el motivo de reemplazar wp-cron de WordPress por un trabajo de cron del sistema. El modo de los archivos que crea el trabajo procede del umask que hereda. Ese valor tampoco es necesariamente el de su shell. Por eso conviene leer cómo umask establece los permisos de los archivos si la salida de un trabajo queda sin permisos de lectura.

Causa 4: la salida se envía a un buzón que nadie lee

cron recopila todo lo que un trabajo escribe en la salida estándar y en la salida de error estándar. Si el trabajo escribió cualquier contenido, cron se lo entrega al sistema de correo local, dirigido al propietario del crontab o a lo que indique MAILTO. En un VPS básico normalmente no hay ningún MTA (agente de transferencia de correo) instalado, por lo que nadie entrega ese mensaje. El error existió durante un momento y después no llegó a ninguna parte. Esa es la razón por la que un trabajo averiado parece silencioso.

Envíe la salida a un archivo que pueda controlar.

0 3 * * * /usr/local/sbin/backup-site.sh >> /var/log/backup-site.log 2>&1

>> añade la salida estándar al archivo. 2>&1 dirige la salida de error estándar al destino que tenga la salida estándar en ese momento, por lo que debe aparecer después de la redirección. Si se escribe al revés, como 2>&1 >> file, la salida de error estándar conserva su destino original y el error que está buscando es precisamente la parte que nunca llega al archivo.

El journal es el otro destino adecuado. logger escribe en syslog usando la etiqueta que elija.

0 3 * * * /usr/local/sbin/backup-site.sh 2>&1 | logger -t backup-site

Léalo de nuevo con journalctl -t backup-site. Así, la salida del propio trabajo queda junto a las entradas de cron y es fácil seguir la secuencia temporal. Si también necesita registrar qué persona ejecutó cada comando en el servidor, ese es un sistema independiente. auditar los comandos de usuario en el servidor lo explica.

MAILTO="" al principio de un crontab desactiva el correo para los trabajos que aparecen después. Establecer MAILTO en una dirección real sólo sirve si existe un MTA operativo. Por tanto, compruebe que el correo sale del servidor antes de depender de él.

Una regla durante la depuración: no añada nunca > /dev/null 2>&1. Es la línea más habitual de todos los crontab y descarta la única evidencia disponible. Vuelva a añadirla después, si quiere, cuando el trabajo funcione.

Causa 5: el script presupone un entorno que cron no proporciona

Una vez localizado el comando y capturada su salida, queda todo lo demás que la sesión le proporciona automáticamente.

  • El shell puede no ser bash. Compruébelo con ls -l /bin/sh. En Debian y Ubuntu apunta a dash, por lo que la prueba con corchetes dobles, los arrays y source fallan con un error de sintaxis. Añada al script una línea #!/bin/bash y ejecútelo, o establezca SHELL al principio del crontab.
  • El directorio de trabajo no es el directorio actual de su sesión. Use rutas absolutas en todas partes o cd al directorio en la primera línea del script. Una ruta relativa es la causa aislada más habitual de que un trabajo «funcione cuando lo ejecuto manualmente».
  • La configuración regional no es la de su sesión. Cualquier comando que dé formato a una fecha o un número, o que ordene texto, puede producir una salida distinta con otro LANG. Si un paso posterior analiza esa salida, establezca la configuración regional en el script en lugar de confiar en el entorno.
  • No hay una TTY (terminal). Un comando que solicita confirmación, abre un editor o muestra una barra de progreso puede quedar bloqueado o finalizar. Añada la opción no interactiva que proporcione la herramienta.
  • No hay un agente SSH. SSH_AUTH_SOCK no está en el entorno de cron, por lo que un comando ssh o rsync que funcionaba porque el agente estaba cargado ahora falla al autenticarse. Asigne al trabajo su propia clave, propiedad del usuario que lo ejecuta.
  • No hay un bus de sesión del usuario, por lo que systemctl --user falla cuando se ejecuta desde cron hasta que se establece XDG_RUNTIME_DIR. Una unidad del sistema es una solución más adecuada.

En Fedora, Rocky y Alma hay otro posible origen. SELinux confina los trabajos de cron, por lo que se deniega un trabajo que accede a una ruta con una etiqueta inesperada aunque los permisos del archivo sean correctos. Compruebe las denegaciones con sudo ausearch -m avc -ts recent y lea Conceptos básicos de SELinux para un servidor antes de desactivarlo.

La prueba de un minuto que muestra el entorno de cron

Deje de suponer qué contiene el entorno de cron y léalo. Escriba un script que vuelque todo, prográmelo cada minuto, espere y lea el archivo.

cat > /home/deploy/cron-probe.sh <<'EOF'
#!/bin/bash
echo "=== probe ==="
date -Is
pwd
id
echo "SHELL=$SHELL"
echo "LANG=$LANG"
command -v node || echo "node is not on this PATH"
env | sort
EOF
chmod +x /home/deploy/cron-probe.sh

Añada una línea al crontab del usuario con el que se ejecuta el trabajo real, usando rutas absolutas a ambos lados.

* * * * * /home/deploy/cron-probe.sh >> /home/deploy/cron-probe.log 2>&1

Espere un minuto y lea /home/deploy/cron-probe.log. Compárelo con los mismos comandos ejecutados en su propia shell. La línea PATH, el directorio de trabajo y la configuración regional suelen explicar por sí solos el fallo. Tenga en cuenta dos detalles de esta configuración: los signos de porcentaje están dentro del script, donde no se aplica la regla de cron, y la ruta del registro permite escribir al usuario del trabajo.

Elimine esa línea del crontab en cuanto tenga la respuesta. Un trabajo que se ejecuta cada minuto y añade contenido a un archivo llenará un disco pequeño, y lo hará sin emitir avisos.

¿El horario es el que pretendía?

Una línea de crontab de usuario comienza con cinco campos: minuto, hora, día del mes, mes y día de la semana. Dos de ellos interactúan de una forma que suele causar confusión.

Cuando el día del mes y el día de la semana están restringidos, es decir, ninguno de los dos es *, cron ejecuta el trabajo cuando coincide cualquiera de los dos campos. 0 0 13 * 5 no significa «viernes 13». Se ejecuta a medianoche el día 13 de cada mes y a medianoche todos los viernes. Para obtener un único día concreto, deje uno de los dos campos como * y compruebe el otro dentro del script.

cron usa la zona horaria del sistema. Muchas imágenes de VPS vienen configuradas con UTC (tiempo universal coordinado), por lo que un trabajo programado para las 03:00 se ejecuta a las 03:00 UTC, que puede ser la mitad de la tarde en su zona horaria. timedatectl muestra la configuración que usa realmente su servidor. Consulte ese valor en lugar de suponer que coincide con el de su portátil.

Conviene conocer otras dos trampas de los horarios. @reboot se activa cuando se inicia el propio cron, que no es necesariamente el momento en que la red está disponible. Por eso, un trabajo que necesite DNS o un host remoto puede fallar durante el arranque y ejecutarse correctamente en todas las ejecuciones manuales posteriores. Además, nada impide que un trabajo lento vuelva a iniciarse mientras la copia anterior todavía está en ejecución. Envuélvalo en un bloqueo.

*/5 * * * * /usr/bin/flock -n /tmp/backup-site.lock /usr/local/sbin/backup-site.sh >> /var/log/backup-site.log 2>&1

flock -n termina inmediatamente cuando el bloqueo ya está ocupado, por lo que la ejecución superpuesta se detiene en lugar de acumularse sobre la primera.

Cuándo systemd timer es la herramienta adecuada

cron sirve bien para una sola cosa: ejecutar este comando a esta hora. En todo lo demás es limitado. Un timer proporciona el journal sin redirecciones, un estado de salida que puede consultar más tarde, ordenación respecto a network-online.target y un retraso aleatorio para que cien servidores no inicien el trabajo en el mismo segundo. Cuando el trabajo necesita cualquiera de estas funciones, un servicio y un timer de systemd en un VPS requieren menos trabajo que mantener una línea de crontab. La parte del servicio le plantea una pregunta que cron nunca plantea: cómo sabe la unidad que el trabajo ha comenzado realmente. Por eso, lea primero qué significa Type= en las unidades simple, forking y notify, porque un script que se convierte en daemon con el tipo predeterminado deja la unidad en estado activo aunque ya no haya ningún proceso asociado. El comportamiento de los reintentos también se define ahí, porque las políticas de reinicio de systemd determinan qué ocurre después de un fallo, y cron no ofrece ninguna respuesta a esa pregunta.

Conserve cron para los trabajos pequeños. Traslade a un timer todo lo que tenga dependencias o una política de reintentos. Ambos pueden ejecutarse en el mismo servidor, así que no tiene que completar esta migración de una sola vez.

FAQ

¿Por qué mi tarea de cron funciona manualmente, pero falla cuando la ejecuta cron?

Porque el entorno de su shell y el de cron son diferentes. Su shell de inicio de sesión lee /etc/profile y ~/.bashrc, que establecen PATH, la configuración regional y las variables de su agente. cron inicia el comando sin nada de eso, desde un directorio de trabajo diferente y, a veces, con otro shell. Use rutas absolutas para cada comando, establezca lo necesario al principio del crontab o dentro del script, y programe una tarea de prueba que se ejecute un minuto después. Esta tarea debe escribir env | sort, pwd y id en un archivo de registro para que pueda leer el entorno real de cron en lugar de intentar adivinarlo.

¿Cómo compruebo si cron ejecutó realmente mi tarea?

Lea el registro del daemon. Use journalctl -u cron en Debian y Ubuntu, o journalctl -u crond en Fedora, Rocky y Alma. Algunas imágenes envían los mensajes mediante rsyslog a un archivo situado bajo /var/log. Busque una entrada en el minuto indicado por la programación y compruebe que incluya el comando. Si no hay ninguna entrada, cron nunca recibió esa programación. Confirme que editó el crontab correcto. Una entrada sin resultado significa que el comando se inició y terminó con un error. Capture su salida mediante una redirección.

¿Por qué date +%Y falla dentro de un crontab?

cron trata % como un carácter especial en el campo del comando. El primer % sin escape termina el comando. Todo lo que aparece después se pasa a ese comando como entrada estándar, y cada % adicional se convierte en una nueva línea. Por eso, un nombre de archivo con una fecha formateada nunca llega al programa para el que lo escribió. Escape cada signo de porcentaje como \%, o mueva el comando a un script y llame al script desde cron. Dentro de un script, el signo de porcentaje no tiene ningún significado especial.

¿Adónde va la salida de mi tarea de cron?

Al sistema de correo local, dirigida al propietario del crontab o a quien indique MAILTO. La mayoría de las imágenes de VPS no tienen instalado ningún agente de transferencia de correo, por lo que el mensaje se descarta y la tarea parece no producir salida. Redirija la salida a un archivo con >> /path/to/log 2>&1. Mantenga ese orden para que el error estándar siga a la salida estándar. También puede canalizarla mediante logger -t myjob y leerla después con journalctl -t myjob. No use > /dev/null 2>&1 mientras siga depurando.

¿Debo usar cron o un temporizador de systemd?

Use cron para ejecutar un comando sencillo a una hora fija, especialmente si necesita trasladarlo a una máquina que no ejecute systemd. Use un temporizador cuando quiera que la salida aparezca en el journal sin redirecciones, consultar el estado de salida, iniciar la tarea después de que la red esté disponible, aplicar un retraso de inicio aleatorio o reintentarla después de un fallo. Ambos pueden ejecutarse en el mismo servidor. Puede trasladar las tareas una por una cuando sea necesario.