CronDrive
Guías

Cómo recibir un correo cuando un cron job falla

Actualizada el 1 de noviembre de 2026

Cron ya puede enviar correos: todo lo que imprime un job se envía a la dirección de MAILTO. En la práctica fallan tres cosas: el servidor no tiene cómo enviar correos, llega un correo por cada ejecución, y un job que falla sin imprimir nada no envía nada. Aquí verás cómo resolver cada una, y cómo avisar a un chat en su lugar.

Cómo funciona MAILTO

MAILTO=tu@example.com
0 3 * * * /ruta/a/backup.sh
  • Cron envía por correo la salida de cada ejecución, tanto la salida estándar como los errores. Si el job no imprime nada, no hay correo.
  • MAILTO se aplica a las líneas que están debajo, hasta que otro MAILTO lo cambie. MAILTO="" desactiva los correos.
  • Sin MAILTO, el correo va al usuario del crontab en la misma máquina, a /var/mail/<usuario>, donde nadie lo lee.

Enviar los correos de cron por SMTP con msmtp

Cron entrega sus correos a /usr/sbin/sendmail, y la mayoría de los servidores no lo tienen. msmtp lo proporciona y envía los correos por una cuenta SMTP, la de tu proveedor de correo o la de un servicio como Amazon SES. En Debian y Ubuntu:

sudo apt install msmtp-mta

Escribe su configuración en el directorio personal del usuario del crontab, en ~/.msmtprc (para el crontab de root, /root/.msmtprc). Reemplaza host, port, from, user y password por los datos de tu proveedor:

defaults
auth on
tls on
syslog on

account default
host smtp.example.com
port 587
from cron@example.com
set_from_header on
user cron@example.com
password la-contraseña
chmod 600 ~/.msmtprc
  • from es el remitente, la cuenta que envía los correos; MAILTO, en el crontab, es quien los recibe. Pueden ser la misma dirección.
  • El archivo tiene la contraseña, así que msmtp se niega a usarlo si otros usuarios pueden leerlo.
  • El puerto 587 con tls on usa STARTTLS. Si tu proveedor usa el puerto 465, agrega tls_starttls off.
  • set_from_header on reemplaza el remitente que escribe cron, como root (Cron Daemon), por la dirección de from. Muchos proveedores solo aceptan direcciones de tu cuenta.
  • Con Gmail, el host es smtp.gmail.com en el puerto 587, y la contraseña es una contraseña de aplicación, que requiere tener activada la verificación en dos pasos.
  • syslog on registra cada correo enviado o rechazado, y puedes leerlo con journalctl -t msmtp.

Prueba primero msmtp, con el usuario del crontab:

printf 'Subject: prueba de msmtp\n\nFunciona.\n' | msmtp tu@example.com

Después prueba cron con una línea que imprima algo cada minuto, y bórrala cuando llegue el correo:

MAILTO=tu@example.com
* * * * * echo "prueba de correo de cron"

Otras distribuciones también tienen msmtp: cron solo necesita que /usr/sbin/sendmail sea msmtp u otro programa que envíe correos.

Solo cuando el job falla

Un job que imprime algo en cada ejecución envía un correo en cada ejecución, y pronto nadie los lee. Hay dos formas de recibirlos solo cuando el job falla.

Con chronic, de moreutils, que oculta la salida cuando el comando sale bien y la muestra cuando falla:

sudo apt install moreutils
MAILTO=tu@example.com
0 3 * * * chronic -v /ruta/a/backup.sh

chronic termina con el mismo código de salida que el job. Usa -v: sin esa opción, un job que falla sin imprimir nada no envía correo. Con ella, la salida llega con etiquetas y termina con RETVAL: y el código de salida, así que siempre se genera salida cuando el job falla, y cron tiene algo que enviar.

Sin chronic, guarda la salida en un archivo e imprímela, con el código de salida, solo cuando el job falla:

MAILTO=tu@example.com
0 3 * * * /ruta/a/backup.sh > /tmp/backup.log 2>&1 || { code=$?; echo "backup.sh falló con código $code"; cat /tmp/backup.log; }

Usa un archivo distinto para cada job, para que dos jobs no escriban en el mismo.

Por qué no llega el correo

  1. El job no imprimió nada. Una ejecución que sale bien sin imprimir, o que falla sin imprimir nada, no tiene nada que enviar. Prueba con la línea del echo de arriba.
  2. No existe /usr/sbin/sendmail. En Debian y Ubuntu, cron registra No MTA installed, discarding output. Búscalo con grep CRON /var/log/syslog o journalctl -u cron (más en los logs de cron).
  3. msmtp no pudo enviarlo. journalctl -t msmtp muestra la respuesta del servidor SMTP: una contraseña incorrecta, un remitente rechazado, un puerto bloqueado.
  4. MAILTO no se aplica a la línea. Está debajo de la línea, está vacío, u otro MAILTO entre ambos lo cambia.
  5. Llegó a spam. Revisa la carpeta de spam, y que el dominio del remitente tenga SPF y DKIM para el servidor que lo envía.
  6. El job corre en un contenedor. Los contenedores rara vez tienen cómo enviar correos. Envía la salida a docker logs (ver cómo ejecutar cron jobs en un contenedor Docker) o avisa a un chat, como se explica abajo.

Avisar a un chat en su lugar

Sin servidor de correo, la línea del crontab puede llamar a un webhook cuando el job falla. En estas líneas, || ejecuta la alerta solo si el job falla, y code=$? guarda primero el código de salida del job, para que el mensaje pueda incluirlo. Para un webhook entrante de Slack:

0 3 * * * /ruta/a/backup.sh || { code=$?; curl -fsS -m 10 -H 'Content-Type: application/json' -d "{\"text\":\"backup.sh falló en $(hostname) con código $code\"}" https://hooks.slack.com/services/...; }

Para un webhook de Discord, la misma línea con content en lugar de text:

0 3 * * * /ruta/a/backup.sh || { code=$?; curl -fsS -m 10 -H 'Content-Type: application/json' -d "{\"content\":\"backup.sh falló en $(hostname) con código $code\"}" https://discord.com/api/webhooks/...; }

Para una notificación en tu teléfono con ntfy, sin crear una cuenta:

0 3 * * * /ruta/a/backup.sh || { code=$?; curl -fsS -m 10 -H "Title: backup.sh falló" -d "En $(hostname), código $code" https://ntfy.sh/tu-tema; }

Para recibirla, instala la aplicación de ntfy en tu teléfono y suscríbete al mismo tema que pusiste en la URL (cómo hacerlo). Cualquiera que conozca un tema de ntfy.sh puede leerlo, así que elige un nombre largo y aleatorio.

Guarda el código con code=$? antes que nada, como en estas líneas: si el crontab usa bash (SHELL=/bin/bash), un $(hostname) escrito antes de $? en el mismo comando lo reinicia, y la alerta dice que el job falló con código 0.

Lo que estas alertas no pueden avisarte

El correo y el webhook los envía la misma línea, en la misma máquina, después del job. Si el job no se ejecuta, porque el servidor está caído, cron está detenido o alguien reemplazó el crontab, o si se cuelga y nunca termina, no hay correo ni mensaje. Para enterarte también de eso, necesitas un servicio que espere una señal del job dentro de un plazo y te avise cuando no llega (más en cómo monitorear cron jobs).

Con CronDrive

Reemplaza la línea por una que llame a CronDrive al inicio y al final, con el código de salida:

0 3 * * * curl -fsS -m 10 https://job.crondrive.com/<token>/start/$$; /ruta/a/backup.sh; curl -fsS -m 10 https://job.crondrive.com/<token>/end/$?/$$

CronDrive envía el correo sin que tu máquina necesite un servidor de correo, y también puede publicar en Slack, Discord, Microsoft Teams, Telegram o PagerDuty. Te avisa cuando el job falla, y también cuando no empieza o no termina en los minutos que permites. Cuando creas el job en CronDrive, te entrega esta línea con el token del job en lugar de <token>.

CronDrive te avisa por email, o en Slack, cuando un job con este horario falla, o no se inicia o no termina a tiempo. Gratis hasta 15 jobs.

Lo que verías con este horario

Un ejemplo con las últimas ejecuciones de este horario y resultados inventados: una falló y otra no se ejecutó.

Horas en UTC.

Hora
dom 1 nov, 03:00 A tiempo Inició 03:00, terminó 03:13, duró 13 min.
sáb 31 oct, 03:00 No se ejecutó Alertas enviadas a las 03:05 y 03:30.
vie 30 oct, 03:00 A tiempo Inició 03:00, terminó 03:14, duró 14 min.
jue 29 oct, 03:00 Falló con código 1 Alerta enviada a las 03:04. Inició 03:00, terminó 03:04, duró 4 min.
mié 28 oct, 03:00 A tiempo Inició 03:00, terminó 03:13, duró 13 min.
mar 27 oct, 03:00 A tiempo Inició 03:00, terminó 03:14, duró 14 min.

La alerta de la ejecución que falló

Email de CronDrive, jue 29 oct, 03:04

Falló (código 1): "Tu job"

El job "Tu job" falló con el código de salida 1.

Si conectas Slack, el mismo mensaje llega a tu canal.

El cambio en tu crontab

0 3 * * * curl -fsS -m 10 https://job.crondrive.com/uG2Exw/start/$$; /path/to/job.sh; curl -fsS -m 10 https://job.crondrive.com/uG2Exw/end/$?/$$

Dos llamadas alrededor de tu comando, en la misma línea. La línea de tu job trae su propio token.

Monitorear este horario