Skip to content

Email (local MTA)

The sendmail notifier hands each message to the machine’s sendmail-compatible program (Postfix, exim, msmtp, ssmtp), the same way crond sends MAILTO mail. There’s no host, port, TLS, or password to set: the mail system already knows where to send and handles queueing and retries.

Use it when the machine already sends mail. If it doesn’t, the SMTP notifier talks to Gmail, SES, or another server directly.

[notifiers.mta]
type = "sendmail"
Key Required What it does
from yes The From: address: "[email protected]" or "Name <[email protected]>".
to yes List of recipients, at least one.
cc no List of Cc: recipients.
bcc no List of Bcc: recipients. The mail system removes them before delivery.
reply_to no A Reply-To: address.
sendmail_path no Absolute path to a specific program. Unset: find the system one.
template_path no A Go template file that replaces the built-in plain-text message.

To send one task’s mail to another address, use "mta:[email protected]" in notify, much like a per-crontab MAILTO. See One channel, several destinations.

Without sendmail_path, RunWisp tries /usr/sbin/sendmail, /usr/lib/sendmail, /usr/bin/sendmail, then sendmail on $PATH, in that order. Every common mail system installs one of these.

Set sendmail_path to use a specific program:

[notifiers.mta]
type = "sendmail"
sendmail_path = "/usr/local/bin/msmtp"

It must be an absolute path. RunWisp looks for the program at the first send, not at startup, so the config still loads on a machine where the mail package isn’t set up yet. If nothing is found, the delivery fails with an error listing the paths it tried.

RunWisp runs <program> -t -i and writes the message to its input. Addresses are read from the headers, never passed as command-line arguments.

Only exit code 75 (temporary failure) is retried. Any other error, like a rejected address or a broken mail setup, fails at once, and the bell gets a notify.delivery_failed warning with the program’s error output.

An exit code of 0 means the mail system accepted the message, not that it was delivered. If accepted mail never arrives, look in /var/log/mail.log and mailq.

Plain text, no markup:

Subject: backup-postgres failed
Date: Thu, 14 May 2026 17:11:04 +0200
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8
Auto-Submitted: auto-generated
X-Auto-Response-Suppress: All
User-Agent: runwisp-notify/1
backup-postgres failed
Exited with code 1 after 0.3s.
Scheduled run · 14 May, 17:11.
Error: connection refused
dial tcp 127.0.0.1:5432: connect: connection refused
View run: https://runwisp.example.com/tasks/backup-postgres/01J...
--
from runwisp · bright-falcon
  • The output block appears for failures and timeouts, up to three lines or 300 bytes.
  • The View run link appears only when external_url is set.
  • Auto-Submitted and X-Auto-Response-Suppress stop out-of-office bots from replying.
  • Subjects with non-ASCII characters are encoded, so accents survive.

Point template_path at your own Go template. Start from the built-in sendmail.tmpl.txt. It gets the whole event and the same helpers as the Slack template. A header value containing a line break is rejected before the message is sent.

runwisp import cron finds MAILTO= in a crontab and prints the notifier block to add. One difference: cron mailed whenever a job printed anything, while RunWisp mails when a run fails. To also get mail for successful runs, add a [[route]] with kinds = ["succeeded"]. See Coming from cron.

  • A missing from, or an empty to.
  • A from, reply_to, to, cc, or bcc value that isn’t a valid email address.
  • A relative sendmail_path.