Guide

Send Email from a Bash Script

Almost every ops script eventually needs to email someone — a backup finished, a disk crossed a threshold, a job failed. The old answer was mailx plus a local MTA, which broke when providers disabled basic auth. The Nylas CLI sends from a shell script with one command over API, no daemon and no SMTP config. This guide covers the one-liner, conditional sends based on exit codes, HTML bodies, and the error handling a production script needs.

Written by Pouya Sanooei Software Engineer

VerifiedCLI 3.1.16 · Gmail, Outlook · last tested June 8, 2026

Command references used in this guide: nylas email send, nylas auth config, and nylas auth login.

How do you send email from a bash script?

You send email from a bash script with nylas email send, passing the recipient, subject, and body as flags and adding --yes to skip the interactive confirmation. That last flag is what makes it scriptable — without it the command waits for a keystroke, which a cron job or CI runner can't provide. The whole send is one line over HTTPS, with no mail server involved.

This replaces the old mailx-plus-MTA approach that quietly died across the industry. After Google disabled “less secure app” passwords in September 2024 and Microsoft retired Basic Authentication in October 2022, scripts that relied on SMTP usernames and passwords started failing. The CLI sends over API with OAuth handled for you, so the same script keeps working through those provider changes.

#!/usr/bin/env bash
set -euo pipefail

# Authenticate once interactively, or use --api-key on a headless server
# nylas auth login --provider google
# nylas auth config --api-key "$NYLAS_API_KEY"

nylas email send \
  --to you@example.com \
  --subject "Backup complete" \
  --body "Nightly backup finished at $(date)." \
  --yes

How do you send only when something happens?

Send conditionally by checking a command's exit code and emailing only on the branch you care about. The idiom is to run the job, capture $?, and call nylas email send in the failure path — so you get an alert when a backup fails, not a message on every run. Conditional sends are what separate a useful alert from inbox noise.

The same pattern handles thresholds: compute a value, compare it, and send if it crosses a line. A disk-usage check that emails only above 85% capacity fires a handful of times a year instead of nightly. Keep the condition explicit in the script so anyone reading it knows exactly when mail goes out.

#!/usr/bin/env bash
# Alert only if the backup job fails
if ! /usr/local/bin/run-backup.sh; then
  nylas email send --to oncall@example.com \
    --subject "FAILED: nightly backup" \
    --body "run-backup.sh exited with code $?. Check the logs." --yes
fi

# Threshold alert: only email above 85% disk usage
usage=$(df / | awk 'NR==2 {print $5}' | tr -d '%')
if [ "$usage" -gt 85 ]; then
  nylas email send --to ops@example.com \
    --subject "Disk at ${usage}%" --body "Root filesystem is filling up." --yes
fi

How do you make the send reliable in production?

Make it reliable by handling the send's own failure: wrap it so a transient API error retries a bounded number of times, and log the outcome rather than letting a failed alert pass silently. An alert that fails quietly is worse than no alert, because you believe you'd be told. A short retry loop with a sleep covers the common transient cases.

On servers, authenticate with nylas auth config --api-key from an environment variable instead of an interactive login, and keep the key out of the script body and out of version control. Set set -euo pipefail at the top so the script fails loudly on an unset variable or a broken pipe, and send an HTML body with --html when the message needs formatting.

# Retry the send a few times on transient failure, then give up loudly
send() {
  local n=0
  until nylas email send --to ops@example.com \
        --subject "$1" --body "$2" --yes; do
    n=$((n+1)); [ "$n" -ge 3 ] && { echo "email send failed" >&2; return 1; }
    sleep 5
  done
}
send "Report ready" "The nightly report is attached."

Next steps