All posts

Cron Email Explained: MAILTO, Alerts, and Why It Fails

September 20, 2026

Ask three developers what "cron email" means and you'll get three different answers. Some mean the automatic message cron sends when a job produces output. Others mean a notification their script fires deliberately when something breaks. Both are valid — and confusing the two is exactly why teams discover their alerting was silently broken, sometimes for weeks.

What Does "Cron Email" Actually Mean?

Cron email has two distinct meanings, and clearing that up matters before troubleshooting anything.

The first is cron's built-in behavior: when a scheduled job produces stdout or stderr, the cron daemon captures it and mails it to whatever address is set in the MAILTO environment variable in your crontab. You didn't write any email logic — cron did it for you, using whatever local mail transport is available.

The second is a script that deliberately sends an email as part of its own logic — calling sendmail, piping through mail, or hitting an email API when a specific condition (like a failed backup) is detected. This is intentional, code-level alerting, separate from cron's automatic output capture. Both fall under "cron email," but they behave very differently when things go wrong.

The Three Ways Cron Jobs Send Email

MAILTO output. This is the zero-code option: set MAILTO=you@example.com at the top of your crontab, and any output from a job gets mailed automatically. It's convenient but depends entirely on a working local mail transport agent (MTA). We've covered the syntax and common failure points in our dedicated MAILTO article — but it's worth knowing this is the mechanism most crontab email setup guides are actually describing.

Piping to a local mail command. Scripts often redirect output directly into mail, sendmail, or a lightweight relay like msmtp instead of relying on cron's automatic capture. Msmtp cron setups are popular because msmtp can relay through an external SMTP account (like Gmail or a transactional provider) without the overhead of running full Postfix. This gives more control than MAILTO — you decide exactly what triggers a message rather than mailing every bit of output.

Calling an HTTP email API. More modern scripts skip local mail infrastructure entirely and call a transactional email API (SendGrid, Postmark, SES) directly over HTTPS. This is why it's the recommended approach when you want to send email from cron jobs without installing a full mail server — no sendmail, no Postfix, just an API call from within the script.

Why Email Alone Is a Fragile Alert Channel

Cron email not working is often invisible until it's too late. Silence is ambiguous — it could mean "everything ran fine" or "nothing sent, and nobody noticed."

Start with the sending side. MAILTO relies on a local MTA (sendmail, Postfix, or similar) being installed and correctly configured. On a fresh server, or a minimal container image, that MTA often doesn't exist at all — so cron's attempt to mail output fails quietly, with no error visible to you.

Even when mail is sent successfully, deliverability isn't guaranteed. Messages sent without proper SPF, DKIM, and DMARC authentication are routinely flagged or rejected by receiving mail servers — transactional email needs the same authentication rigor as marketing email to reliably reach an inbox. A server-generated failure alert sent from an unauthenticated local MTA is a prime candidate for the spam folder or a hard bounce.

Volume creates its own problems. A job that fails repeatedly and emails on every run can trigger rate limiting or temporary blocks from your mail provider, producing SMTP errors that stop delivery mid-incident, exactly when you need alerts most.

And there's the single point of failure nobody plans for: if the mail server itself — local or upstream — goes down, every alert dependent on it goes down too. Cron job monitoring built entirely on one delivery path has no way to signal that the path itself is broken.

The Fix: Pair Email With an Independent Watchdog

Email should stay in your alerting stack — it's just not sufficient alone. The fix is pairing it with a monitoring channel that doesn't depend on your SMTP stack at all: a dead man's switch, also called heartbeat monitoring.

The pattern is simple. Your cron job pings an external monitoring endpoint on every successful run. If that ping doesn't arrive on schedule, the monitoring service — not your mail server — raises the alert, through channels like Slack, SMS, or a separate email domain entirely. This catches two failure modes email alone misses: cron never running the job at all, and the mail server being the actual point of failure.

This is genuine cron job monitoring, not just output capture. It treats "did this run and succeed" as a fact to verify externally, rather than a message to hope arrives. For teams building this out, our guides on avoiding silent failures with a monitoring framework and automated health checks walk through implementation in more depth.

Quick Setup Checklist for Reliable Cron Email Alerts

  • Set MAILTO only for the specific person or alias who should see failures — don't broadcast routine output to a whole team.
  • Redirect stdout for noisy jobs (>> logfile.log) so cron only emails when something is actually wrong, not on every routine run.
  • Test the mail path once deliberately — trigger a failure and confirm the message actually lands in an inbox, not just that the command exits cleanly.
  • Add a second, non-email channel (heartbeat ping, SMS, chat webhook) so a dead mail server doesn't mean a dead alert.
  • Review the crontab periodically — stale MAILTO addresses and abandoned jobs are a common source of alerts nobody's watching.

This is a starting checklist, not a full framework — for the bigger picture on why unattended "set and forget" cron jobs tend to fail silently regardless of alerting setup, it's worth reading in full.

Frequently Asked Questions

What does MAILTO in crontab actually do?

MAILTO is an environment variable set inside a crontab that tells the cron daemon where to send a job's stdout and stderr output. It doesn't require any code in your script — cron captures the output automatically and relays it through the local mail transport agent to the address you specify.

Why isn't cron sending emails on my server?

The most common cause is a missing or unconfigured local MTA — many servers, especially minimal or containerized ones, don't ship with sendmail or Postfix installed by default. Even with an MTA present, misconfigured relay settings or missing authentication can cause messages to fail silently without any visible error.

Is it better to email on every run or only on failure?

Email only on failure for most jobs. Mailing on every successful run creates alert fatigue, buries genuine failures in routine noise, and increases the chance of hitting provider rate limits during an actual incident.

What happens if the mail server itself goes down — will I still get alerted?

No, not if email is your only alert channel — a downed mail server means silence, indistinguishable from a job that ran successfully. This is why pairing email with an independent heartbeat or dead man's switch monitoring service matters: it alerts you through a completely separate path.

How do I send email from a cron job without installing a full mail server?

Use a lightweight relay like msmtp configured against an external SMTP account, or call a transactional email API (SendGrid, Postmark, SES) directly over HTTPS from within your script. Both approaches avoid the overhead of installing and maintaining Postfix or sendmail locally.

Your email alert is only as good as your inbox — and your inbox is only as good as the mail server behind it. Add an independent heartbeat check alongside your existing MAILTO or script-based alerts, not instead of them, so a broken SMTP stack or a silent cron daemon can't hide a failure from you. Cronevra gives you ping-based monitoring that doesn't depend on email delivery at all — check the pricing page to add it to your stack today.