# Send email from Docker without SMTP

Source: https://developer.nylas.com/docs/cookbook/email/send-email-from-docker/

You package an app into a Docker image, ship it to a cluster, and the first time it tries to send a notification the connection to your SMTP server hangs and times out. The container can't reach port 25, 465, or 587, and adding postfix to the image just moves the failure inside the container. Most managed hosts, including AWS, GCP, and Azure, block outbound SMTP by default to cut spam, so the mail server you tested locally is unreachable in production.

This guide skips SMTP entirely. You send email from inside a container through one HTTPS POST to a REST endpoint, with the API key passed as an environment variable. No mail transfer agent, no port 25, no local relay to babysit.

## How do I send email from a Docker container without SMTP?

Send a single HTTPS POST to `POST /v3/grants/{grant_id}/messages/send` over port 443, which every container can reach. You pass the recipient, subject, and body as JSON, and the API delivers the message through the connected account's provider. Because it rides on HTTPS, the SMTP ports your host blocks never enter the picture.

The request below sends a plain notification email. It targets the send endpoint, which works the same across Gmail, Microsoft 365, Yahoo, iCloud, and IMAP, so one container image reaches every provider your users connect. For a grant-based send, `to` is the only recipient field you must set; the sender defaults to the connected account, and `subject` and `body` are optional but you'll almost always set them.

```bash
curl --request POST \
  --url 'https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/messages/send' \
  --header "Authorization: Bearer $NYLAS_API_KEY" \
  --header 'Content-Type: application/json' \
  --data '{
    "subject": "Deploy finished",
    "body": "Build 1421 shipped to production at 14:02 UTC.",
    "to": [{ "name": "On-call", "email": "oncall@example.com" }]
  }'
```

A successful call returns `200` with the sent message object, including its `grant_id` and `id`. If you set `send_at` for scheduled delivery, the API returns `202` instead, since the message is queued rather than sent immediately.


> **Info:** 
> **New to Nylas?** Start with the [quickstart guide](/docs/v3/getting-started/) to set up your app and connect a test account before continuing here.


## How do I send email from a terminal without setting up postfix or sendmail?

Run the same `curl` from any shell. The send endpoint replaces the local mail transfer agent, so you don't install postfix or sendmail, configure a relay host, or maintain `/etc/aliases`. A bare `alpine` image with `curl` added weighs roughly 8 MB, versus the extra packages postfix drags in, and that smaller surface removes a whole class of mail-config bugs.

This pattern fits cron jobs, CI steps, and one-off scripts the same way it fits a long-running service. The endpoint accepts HTML in the `body` field, so terminal scripts can send formatted reports, not just plain text. The example below sends an HTML body, useful when a nightly job emails a summary table or a styled status block.

```bash
curl --request POST \
  --url 'https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/messages/send' \
  --header "Authorization: Bearer $NYLAS_API_KEY" \
  --header 'Content-Type: application/json' \
  --data '{
    "subject": "Nightly report",
    "body": "<h2>Backup complete</h2><p>3 volumes, 0 errors.</p>",
    "to": [{ "email": "ops@example.com" }]
  }'
```

For a fuller terminal walkthrough including scheduling, see [send email from a Bash script or cron job](/docs/cookbook/cli/send-email-bash-cron/).

## What can I use instead of Send-MailMessage in PowerShell?

Use `Invoke-RestMethod` against the send endpoint. Microsoft marked `Send-MailMessage` obsolete, and its docs warn the cmdlet "can't guarantee secure connections to SMTP servers." A REST call sidesteps SMTP completely, which matters in a Windows container where no SMTP relay exists. You build a hashtable, convert it to JSON, and POST it.

The script below reads the API key from an environment variable, so the secret never sits in source. It sends one message and works identically on Windows, Linux, and macOS containers. The `to` field takes an array of address objects, mirroring the JSON shape the API expects, and you can add `cc` or `bcc` arrays the same way.

```powershell
$headers = @{ Authorization = "Bearer $env:NYLAS_API_KEY" }
$body = @{
  subject = "Service restarted"
  body    = "worker-3 came back up at 09:14 UTC."
  to      = @(@{ email = "oncall@example.com" })
} | ConvertTo-Json

Invoke-RestMethod -Method Post `
  -Uri "https://api.us.nylas.com/v3/grants/$env:NYLAS_GRANT_ID/messages/send" `
  -Headers $headers -ContentType "application/json" -Body $body
```

Microsoft documents the obsolete status in the [Send-MailMessage reference](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/send-mailmessage) and points to `Send-MgUserMail` (the Microsoft Graph PowerShell SDK) and MailKit as replacements. A REST send is a provider-agnostic option that avoids SMTP entirely, which is what a container without a relay needs.

## SMTP relay vs the Email API in a container

An SMTP relay needs an open outbound port, a mail library or local agent, and connection retries when the relay is slow. The REST approach needs only an HTTPS client, which every base image already has through `curl`. The table compares both on the work a containerized send actually requires. The unified column is the path this guide uses.

| Task | SMTP relay in a container | Nylas Email API |
| --- | --- | --- |
| **Outbound port** | 25, 465, or 587, often blocked | 443, always open |
| **In-image dependency** | postfix, sendmail, or an SMTP library | `curl` only |
| **Auth** | Relay username and password | `Authorization: Bearer` API key |
| **Delivery feedback** | Parse SMTP response codes | `200`/`202` plus webhooks |
| **Provider reach** | One relay endpoint | Gmail, Microsoft 365, Yahoo, iCloud, IMAP |

Be honest about the tradeoff: if you already run a trusted internal relay that your container can reach on a private network, and you only send to one domain, SMTP is simpler and has no per-message cost. The REST path wins when the network blocks SMTP, when you send across providers, or when you want delivery webhooks instead of parsing response codes.

## How do I keep the API key out of the image?

Pass the key as an environment variable at runtime, never baked into a layer. A secret written with `ENV` or copied into the image stays in the image history across all of its layers, and `docker history` reveals it to anyone who pulls the image. Inject it with `docker run -e`, a Compose `environment` block, or a Kubernetes Secret, so the value lives only in the running container.

The send endpoint reads `Authorization: Bearer <NYLAS_API_KEY>`, and the scripts above pull that from `$NYLAS_API_KEY`. At runtime you supply it like `docker run -e NYLAS_API_KEY=... your-image`. In Kubernetes, mount a Secret through `envFrom` so the key never appears in the manifest or in `docker inspect`. Rotating the key then means updating one secret, not rebuilding and redeploying the image.

```bash
docker run --rm \
  -e NYLAS_API_KEY="$NYLAS_API_KEY" \
  -e NYLAS_GRANT_ID="$NYLAS_GRANT_ID" \
  your-image:latest
```

For the wider problem of hosts that drop SMTP traffic, see [why SMTP ports are blocked and how to work around it](/docs/cookbook/email/smtp-ports-blocked/) and the [send email without SMTP](/docs/cookbook/use-cases/build/send-email-without-smtp/) overview.

## What's next

- [Send email without SMTP](/docs/cookbook/use-cases/build/send-email-without-smtp/) for the REST-versus-SMTP overview across environments
- [Why SMTP ports are blocked](/docs/cookbook/email/smtp-ports-blocked/) to understand port 25, 465, and 587 restrictions on cloud hosts
- [Send email from a Bash script or cron job](/docs/cookbook/cli/send-email-bash-cron/) for scheduled terminal sends and scripting patterns
- [Getting started with Nylas](/docs/v3/getting-started/) to create a project, connect an account, and get a grant ID