Symptom
An application on OCI sends alerts, password resets or reports through OCI Email Delivery, and some or all messages never arrive. The app may log an SMTP error, or it may report success while the recipient sees nothing.
Every message goes through two stages, and you need to know which one failed:
- Submission. Your app connects to the Email Delivery endpoint, authenticates and hands over the message. Failures here return an SMTP or HTTP error to the app.
- Relay. Email Delivery passes the message to the recipient's mail server. Failures here (bounces, complaints, suppression) are not returned to your app. You only see them in the Email Delivery logs.
export TENANCY=<tenancy-ocid>
export C=<compartment-ocid-of-the-approved-sender>
Quick map: error to cause
| What you see | Stage | Cause | Fix |
|---|---|---|---|
535 Authentication credentials invalid | Submission | Wrong SMTP username or password, or the credential was deleted | Create a new SMTP credential and update the app |
535 Authorization failed: address <x> not authorized | Submission | From address isn't an approved sender, the sender is in a compartment the user's policy doesn't cover, or the user isn't in the group | Add the approved sender and check the email-family policy |
421 Too many auth failures, try again later | Submission | IP throttled after repeated bad credentials or an unapproved sender | Fix the credential or sender, then wait before retrying |
455 Maximum messages sent per minute/day reached | Submission | Send rate or daily limit hit | Slow the sender or request a limit increase |
552 Exceeds byte limit | Submission | Message too large | Shrink attachments, or send a link instead |
254 4.7.1 Suppression for user ... to <recipient> | Submission (accepted, not sent) | Recipient is on the suppression list | Find out why, then remove the suppression |
| Connection timeout | Network | Port blocked, wrong endpoint or region | Test the endpoint, use 587 |
| App reports success, nothing arrives | Relay | Bounce, spam filtering, missing DKIM/SPF | Check the outboundrelayed log |
1. Check connectivity and the endpoint
The SMTP endpoint is regional: smtp.email.<region>.oci.oraclecloud.com. Your app must use the endpoint for the region where the approved sender lives. Confirm it for your tenancy:
oci email configuration get-email --compartment-id $TENANCY
Email Delivery accepts ports 25 and 587 (STARTTLS) and 465 (implicit TLS), and requires TLS 1.2. Use 587. Outbound port 25 is often blocked from cloud networks.
Test from the instance or pod that sends the mail:
openssl s_client -starttls smtp -crlf -quiet \
-connect smtp.email.eu-frankfurt-1.oci.oraclecloud.com:587
You should see a 250 EHLO response. If the connection hangs, check the route table and NSG or security list egress for TCP 587. Private subnets need a NAT gateway, because the endpoint is public.
2. Fix authentication: SMTP credentials
SMTP credentials are separate from the console password, API keys and auth tokens. The password is shown once, when you create the credential.
oci iam smtp-credential list --user-id <user-ocid>
oci iam smtp-credential create --user-id <user-ocid> --description "app-prod smtp 2026-10"
Use a dedicated service user for each application, not a person's account. When someone leaves and their user is deleted, every app using their credential stops sending mail.
Test the new credential outside the app with Python:
import smtplib, ssl
from email.message import EmailMessage
msg = EmailMessage()
msg["From"] = "alerts@ops.example.com" # must be an approved sender
msg["To"] = "oncall@example.com"
msg["Subject"] = "OCI Email Delivery test"
msg.set_content("Test from the app host.")
with smtplib.SMTP("smtp.email.eu-frankfurt-1.oci.oraclecloud.com", 587, timeout=20) as s:
s.starttls(context=ssl.create_default_context())
s.login("<smtp-username>", "<smtp-password>")
s.send_message(msg)
print("accepted")
If this works and the app doesn't, the problem is in the app configuration: wrong host, port, TLS mode or a stale password.
3. Fix authorization: approved senders and policy
The From address must be an approved sender, and the envelope sender (MAIL FROM) should be the same address. Mismatches cause 535 Authorization failed or hurt deliverability.
oci email sender list --compartment-id $C --email-address alerts@ops.example.com
oci email sender create --compartment-id $C --email-address alerts@ops.example.com
Check that the SMTP user's group can use Email Delivery in that compartment:
Allow group app-mail-senders to use email-family in compartment ops-prod
Tip
If the approved sender sits in a different compartment from the one the policy covers, you get the same
535 not authorizederror as a missing sender. Check both.
4. Find out what happened after submission
Turn on the Email Delivery logs before you need them. They're enabled per email domain, with two categories:
outboundaccepted: submission results, includingAuthorization failure,Recipient suppressed, rate limits and parse errors.outboundrelayed: delivery results, withdata.actionset torelay,bounce,complaintorunsubscribe.
Console: Developer Services → Email Delivery → Email Domains → your domain → Logs → Enable log.
Search for bounces to a recipient over the last day:
oci logging-search search-logs \
--search-query "search \"$C/<log-group-ocid>/<relayed-log-ocid>\" | where data.recipient = 'oncall@example.com' | sort by datetime desc" \
--time-start "$(date -u -d '-24 hours' +%FT%TZ)" --time-end "$(date -u +%FT%TZ)"
Useful fields: data.action, data.errorType (hard or soft), data.bounceCategory, data.smtpStatus (the remote server's reply), data.dkimSelector and data.messageId.
| Relay result | Meaning | Action |
|---|---|---|
relay with 250 status | Recipient's server accepted it | Ask the recipient to check spam and quarantine. Check DKIM and SPF |
bounce, hard, bad-mailbox | Address doesn't exist | Fix the address. It is now suppressed |
bounce, soft | Mailbox full, greylisting, temporary block | Usually retried. Watch for repeats |
bounce, spam-related | Recipient's server rejected the content or reputation | Fix DKIM/SPF, review the content |
complaint | Recipient marked it as spam | Address is suppressed. Don't remove without consent |
5. Clear a suppression
A hard bounce or complaint adds the address to the tenancy's suppression list. Suppressions are listed at tenancy level:
oci email suppression list --compartment-id $TENANCY --email-address oncall@example.com
oci email suppression delete --suppression-id <suppression-ocid>
Warning
Only remove a suppression after you've fixed the cause, such as a mistyped address or a mailbox that has since been created. If you keep sending to bouncing or complaining addresses, sender reputation drops and mail to everyone else starts landing in spam.
6. Improve deliverability: DKIM and SPF
Mail without DKIM is far more likely to be filtered. Check the DKIM state on the email domain:
oci email domain list --compartment-id $C --output table
oci email dkim list --email-domain-id <email-domain-ocid> --output table
A DKIM key in NEEDS_ATTENTION usually means the CNAME <selector>._domainkey.<email-domain> is missing or was created at the wrong name. Some DNS providers append the zone automatically, which produces ...example.com.example.com. Enter the zone-relative name instead.
For SPF, copy the include value from the console (Approved senders → View SPF) into the domain's TXT record. The value depends on the region, so don't copy it from another tenancy.
Prevent it next time
- Enable both Email Delivery log categories on every email domain, and alarm on
bounceandcomplaintspikes. - Use one service user per application, store its SMTP password in OCI Vault, and rotate it on a schedule.
- Configure DKIM and SPF before go-live, and set the envelope sender to the same address as
From. - Check your send rate and daily limits against peak volume. Check the current limits in the Email Delivery documentation rather than assuming them.
- Send critical alerts through a second channel (OCI Notifications to PagerDuty or Slack), so a mail problem doesn't hide an outage.