All posts

Cron MAILTO Explained: Syntax, Failures & Limits

August 18, 2026

Anyone who has managed scheduled jobs on Linux has typed MAILTO=you@example.com at the top of a crontab and assumed "did my job run okay" was solved. It isn't. The cron MAILTO variable is genuinely useful, but it's also one of the most misunderstood — and most silently broken — pieces of the traditional cron toolkit. This guide covers the exact syntax, the real reasons it fails in production, and why it was never designed to be a monitoring system.

What the MAILTO Variable Actually Does

MAILTO is a crontab environment variable that tells cron where to email a job's output. By default, when a cron job produces output on stdout or stderr, cron mails that output to the local user who owns the crontab. Setting MAILTO redirects that mail to a different address — or addresses — instead of dumping it into a local mailbox nobody checks.

The key detail people miss: MAILTO only triggers an email when a job actually generates output. A job that runs successfully and prints nothing sends no mail, MAILTO or not. Silence doesn't mean success — it just means nothing was printed. That distinction is the root of most "why didn't I get an email" confusion later on.

Correct MAILTO Syntax and Placement

The crontab MAILTO syntax is straightforward, but placement and quoting rules trip people up regularly.

Set it as a plain variable assignment near the top of the crontab file:

MAILTO=admin@example.com
0 3 * * * /opt/scripts/backup.sh

Cron applies MAILTO to every job defined below that line, until the variable is redefined further down the file. That means a single crontab can send different jobs' output to different addresses simply by resetting MAILTO between blocks of entries.

For multiple addresses, quote the value and separate recipients with commas:

MAILTO="ops@example.com,oncall@example.com"

Some cron implementations are picky about spaces after commas, so keep it tight. To disable cron email entirely for a section of jobs, set an empty string:

MAILTO=""

An empty MAILTO suppresses mail delivery even if the job produces output — useful for noisy jobs where output isn't worth alerting on. Leaving MAILTO unset falls back to cron's default: mail goes to the crontab owner's local system account, often a mailbox nobody has checked in years. For a broader refresher on crontab fields and scheduling symbols, the crontab cheat sheet covers the syntax MAILTO sits inside.

Common Reasons MAILTO Fails Silently

This is where most "cron mailto not working" reports actually originate, and it's rarely a syntax mistake.

No local MTA installed. Cron doesn't send email itself — it hands the message to a local mail transfer agent like sendmail, postfix, or ssmtp. On minimal server images, container base images, and many cloud VMs, no MTA exists at all. Cron tries to deliver, fails silently or logs an error nobody reads, and no email ever leaves the box.

Root mail not forwarded. Even when an MTA is present, mail addressed to root or a local system user often sits in /var/mail/root unless a .forward file redirects it outward. If MAILTO wasn't set, that mail may exist on disk while nobody ever sees it.

Quoting and parsing errors. A malformed MAILTO line — unmatched quotes, stray characters, an unescaped comma outside quotes — can break parsing for that line and, in some cron implementations, cause the entire crontab to be rejected or partially ignored. The failure mode isn't "no email for this job," it's "some jobs stopped running."

Output redirected to /dev/null. Plenty of scripts pipe their own output to /dev/null for cleanliness. If there's no stdout or stderr, MAILTO has nothing to send — by design, but easy to forget months later.

Spam filtering. Mail from a server hostname with no reverse DNS, no SPF, and no DKIM record fits the exact profile spam filters are built to catch. The message can leave the server successfully and still never reach an inbox.

Troubleshooting crontab MAILTO issues usually means checking all five in order: MTA presence, forwarding rules, crontab syntax validity, output redirection, and mail server logs — before assuming the crontab itself is wrong.

Getting Only Error Emails, Not Every Run

A common ask is a cron job email on error only, rather than a message every run. The trick is separating stdout from stderr rather than suppressing output entirely:

0 * * * * /opt/scripts/sync.sh > /dev/null

This discards routine stdout logging but leaves stderr untouched, so cron mailto stderr output — the actual error messages — still triggers mail. Combined with a well-behaved script that only writes to stderr on genuine failure, this cuts email volume dramatically without losing the alerts that matter. It depends entirely on your script writing to the right stream.

Why MAILTO Isn't Real Cron Monitoring

Here's the structural problem: MAILTO can only report on jobs that ran and produced output, through an MTA that works, over a delivery path that isn't blocked by spam filtering. That's three points of failure stacked on top of each other, and every one is silent by default.

None of that says anything about a job that never ran at all — because the server rebooted and cron never restarted, because a deploy overwrote the crontab, or because the process hung indefinitely instead of exiting. A hung job produces no error, no timeout email, nothing. This is the classic cron job silent failure: the schedule looks fine, the crontab looks fine, and the job simply isn't happening.

This is the real difference in cron monitoring vs email alerts. Email-based alerting, including MAILTO, is a push mechanism dependent on the job itself succeeding at sending a message. Ping-based or HTTP-based monitoring flips that model: your job (or Cronevra's scheduler check) expects a signal on a schedule, and raises an alert the moment that signal doesn't arrive — whether the job failed, hung, or never started. It detects absence, not just output. That distinction is covered in more depth in Dead Man's Snitch Explained, and for a more complete way to verify a job's health, see the 4-layer verification checklist.

MAILTO will tell you when a job produces output or an error — if mail delivery cooperates. It says nothing when a job stops running altogether, hangs mid-execution, or the mail queue itself is broken. Cronevra closes that gap with HTTP-ping-based monitoring that doesn't depend on your server's mail configuration at all: jobs check in on schedule, and missed check-ins trigger alerts regardless of whether email was ever going to work. Check the pricing page to see how it fits alongside — or in place of — the crontabs you're already running.

Frequently Asked Questions

Why isn't my cron MAILTO variable sending any emails?

The most common cause is a missing local mail transfer agent (MTA) like sendmail or postfix — cron hands mail off to it, and if it doesn't exist, delivery fails silently. Other frequent causes include a job that produces no output, output redirected to /dev/null, or a malformed MAILTO line breaking crontab parsing.

How do I stop cron from sending emails for a specific job?

Set MAILTO="" above that job in the crontab to suppress mail entirely, or redirect the job's own output to /dev/null so cron has nothing to send. The empty-string approach is cleaner since it doesn't rely on the script's redirection behavior.

Can I send cron output to multiple email addresses with MAILTO?

Yes — quote the value and separate addresses with commas, like MAILTO="ops@example.com,oncall@example.com". Avoid extra spaces after commas, since some cron implementations parse the line strictly.

Does MAILTO work if my server doesn't have a mail server installed?

No. Without a local MTA such as sendmail, postfix, or ssmtp, cron has no way to actually transmit the message, and the mail is dropped silently with no error shown to the user. This is one of the most common reasons MAILTO appears broken on minimal server or container images.

How do I get cron to email me only when a job fails, not on every run?

Redirect stdout to /dev/null while leaving stderr untouched, for example command > /dev/null, so only error output triggers a MAILTO email. This depends on your script writing genuine failures to stderr and routine logging to stdout.

Where does MAILTO need to be placed in the crontab file?

MAILTO must be set as a variable assignment above the job entries it should apply to, and it stays in effect for every job below it until redefined. Placing it after the jobs it's meant to cover has no effect on those earlier entries.