# How Ghost Sends Emails and Which Email Providers Are Supported

> Discover how Ghost sends emails using a dual-path architecture for transactional and bulk messages. Learn about supported email providers like Mailgun.

- Repository: [Ghost/Ghost](https://github.com/TryGhost/Ghost)
- Tags: how-to-guide
- Published: 2026-05-18

---

**Ghost delivers email through a nodemailer-based dual-path architecture: transactional messages (magic links, verifications) route through the `GhostMailer` class supporting direct, SMTP, and Mailgun transports, while bulk newsletters utilize the `EmailServiceWrapper` with `MailgunEmailProvider` as the sole provider implementation.**

Ghost (TryGhost/Ghost) manages email via two specialized pipelines built on nodemailer. Understanding how Ghost sends emails and which email providers are supported ensures reliable delivery for both member authentication flows and mass newsletter campaigns.

## Ghost Email Architecture Overview

Ghost separates email delivery into distinct **transactional** and **bulk** paths. Transactional emails cover one-off system messages like login magic links, member verifications, and error reports. Bulk emails handle newsletter distributions to thousands of members simultaneously. This separation allows optimized transport strategies for each use case.

## Transactional Email Path

### GhostMailer and Nodemailer Integration

The `GhostMailer` class in [`ghost/core/core/server/services/mail/ghost-mailer.js`](https://github.com/TryGhost/Ghost/blob/main/ghost/core/core/server/services/mail/ghost-mailer.js) creates a nodemailer transport based on the `mail.transport` configuration setting. This class instantiates the appropriate transport driver and dispatches single-recipient messages immediately.

When initialized, `GhostMailer` reads `config.mail.transport` and constructs the transport using nodemailer’s factory methods. For Mailgun-specific transports, it automatically injects Mailgun headers (`h:*`) and tags (`o:tag`) into the message payload.

### Supported Transactional Transports

Ghost supports four distinct transport modes for transactional mail:

- **direct** – Built-in nodemailer direct transport that attempts delivery without external relay. Used as a fallback when no provider is configured, primarily for development environments.
- **SMTP** – Standard SMTP relay supporting any compliant server (Postfix, Amazon SES, SendGrid). Configuration passes through `config.mail.options`.
- **mailgun** – Uses `nodemailer-mailgun-transport` to forward messages via Mailgun’s HTTP API rather than SMTP.
- **stub** – No-op transport used exclusively by the test suite in [`ghost/core/core/server/services/mail/ghost-mailer.test.js`](https://github.com/TryGhost/Ghost/blob/main/ghost/core/core/server/services/mail/ghost-mailer.test.js).

If `mail.transport` is omitted from configuration, Ghost automatically falls back to the **direct** transport.

## Bulk Email (Newsletter) Path

### EmailServiceWrapper Orchestration

For newsletter distributions, Ghost employs the `EmailServiceWrapper` located at [`ghost/core/core/server/services/email-service/email-service-wrapper.js`](https://github.com/TryGhost/Ghost/blob/main/ghost/core/core/server/services/email-service/email-service-wrapper.js). This wrapper wires together the **EmailRenderer**, **BatchSendingService**, and an **IEmailProviderService** implementation to handle high-volume delivery.

The wrapper manages recipient segmentation, HTML rendering with member-specific variables, and provider delegation. Unlike the transactional path, bulk email requires a concrete provider implementation that supports batch operations and event tracking.

### MailgunEmailProvider Implementation

Currently, `MailgunEmailProvider` in [`ghost/core/core/server/services/email-service/mailgun-email-provider.js`](https://github.com/TryGhost/Ghost/blob/main/ghost/core/core/server/services/email-service/mailgun-email-provider.js) is the sole implementation of the bulk email interface. This provider delegates HTTP requests to `MailgunClient` ([`ghost/core/core/server/services/lib/mailgun-client.js`](https://github.com/TryGhost/Ghost/blob/main/ghost/core/core/server/services/lib/mailgun-client.js)), which:

- Constructs Mailgun-compatible message payloads
- Enforces batch size limits (default 1000 recipients per request)
- Manages domain warming strategies
- Fetches delivery events and suppression list data

The presence of Mailgun configuration keys (`mailgun_api_key`, `mailgun_domain`) automatically enables this bulk-email path, even if the transactional transport uses SMTP.

## Configuration Examples

### SMTP Configuration for Transactional Mail

Configure any SMTP server in [`config.production.json`](https://github.com/TryGhost/Ghost/blob/main/config.production.json):

```json
{
    "mail": {
        "from": "info@myblog.com",
        "transport": "SMTP",
        "options": {
            "host": "smtp.sendgrid.net",
            "port": 587,
            "auth": {
                "user": "SG.username",
                "pass": "SG.password"
            }
        }
    }
}

```

### Mailgun Settings for Bulk Email

Mailgun requires additional settings beyond the transport configuration:

- `mailgun_api_key` – Your Mailgun API key
- `mailgun_domain` – Verified sending domain
- `mailgun_base_url` – Optional API endpoint override

These settings activate the `MailgunEmailProvider` for newsletter delivery regardless of the transactional transport setting.

## Code Implementation Examples

### Sending Transactional Email with GhostMailer

```javascript
const GhostMailer = require('@tryghost/ghost-mailer');

(async () => {
    const mailer = new GhostMailer();
    
    await mailer.send({
        subject: 'Welcome to Ghost!',
        html: '<p>Hello, thanks for joining.</p>',
        to: 'newmember@example.com',
        from: 'noreply@example.com'
    });
    
    console.log('Transactional message queued');
})();

```

### Dispatching Bulk Newsletters via EmailServiceWrapper

```javascript
const EmailServiceWrapper = require('@tryghost/email-service-wrapper');

(async () => {
    const wrapper = new EmailServiceWrapper();
    wrapper.init();
    
    const email = await wrapper.service.create({
        subject: 'Monthly Update',
        html: '<h1>Our news...</h1>',
        newsletter_id: 1,
        email_provider_id: null
    });
    
    await wrapper.service.send(email.id);
    console.log('Newsletter batch scheduled');
})();

```

### Adding Custom Tags to Bulk Emails

```javascript
await wrapper.service.send(email.id, {
    tags: ['campaign-spring-2025']
});

```

The `MailgunEmailProvider` sanitizes these tags (maximum 10 allowed) and prefixes them as `o:tag` parameters in the Mailgun payload.

## Key Source Files

- **[`ghost/core/core/server/services/mail/ghost-mailer.js`](https://github.com/TryGhost/Ghost/blob/main/ghost/core/core/server/services/mail/ghost-mailer.js)** – Transactional mailer creating nodemailer transports and sending one-off messages.
- **[`ghost/core/core/server/services/email-service/email-service-wrapper.js`](https://github.com/TryGhost/Ghost/blob/main/ghost/core/core/server/services/email-service/email-service-wrapper.js)** – Orchestrates bulk-email pipeline, wiring renderer, batch sender, and provider.
- **[`ghost/core/core/server/services/email-service/mailgun-email-provider.js`](https://github.com/TryGhost/Ghost/blob/main/ghost/core/core/server/services/email-service/mailgun-email-provider.js)** – Implements bulk email provider interface for Mailgun.
- **[`ghost/core/core/server/services/lib/mailgun-client.js`](https://github.com/TryGhost/Ghost/blob/main/ghost/core/core/server/services/lib/mailgun-client.js)** – Low-level HTTP client handling Mailgun API communication, batching, and event fetching.
- **[`ghost/core/core/server/services/email-service/sending-service.js`](https://github.com/TryGhost/Ghost/blob/main/ghost/core/core/server/services/email-service/sending-service.js)** – Facade delegating to configured provider’s `send` method.

## Summary

- Ghost uses **nodemailer** as the foundation for all email delivery.
- **Transactional emails** support four transports: **direct**, **SMTP**, **mailgun**, and **stub**, managed by `GhostMailer`.
- **Bulk newsletters** currently support only **Mailgun** via `MailgunEmailProvider` and `MailgunClient`.
- Configuration is driven by `config.mail.transport` for transactional mail and Mailgun-specific settings (`mailgun_api_key`, `mailgun_domain`) for bulk operations.
- Without explicit configuration, Ghost falls back to the **direct** transport for transactional mail.

## Frequently Asked Questions

### What email providers does Ghost support for newsletters?

Ghost supports **Mailgun** exclusively for newsletter delivery. The `MailgunEmailProvider` class in [`ghost/core/core/server/services/email-service/mailgun-email-provider.js`](https://github.com/TryGhost/Ghost/blob/main/ghost/core/core/server/services/email-service/mailgun-email-provider.js) is currently the only implementation of the `IEmailProviderService` interface, handling batch sending through the Mailgun REST API.

### Can I use SMTP instead of Mailgun for bulk emails in Ghost?

No. While SMTP works for transactional emails via `GhostMailer`, the bulk email path requires Mailgun. The `EmailServiceWrapper` specifically instantiates `MailgunEmailProvider`, which relies on the HTTP API rather than SMTP protocols, enabling features like batch size management and delivery event tracking.

### How do I configure Mailgun for both transactional and bulk emails?

Set `config.mail.transport` to `"mailgun"` for transactional messages, and ensure your Ghost settings include `mailgun_api_key` and `mailgun_domain`. These settings automatically enable the bulk email path. Alternatively, you can use SMTP for transactional mail while keeping Mailgun configured for newsletters.

### What is the GhostMailer class used for?

`GhostMailer` handles **transactional** one-off emails such as member invitations, login magic links, and error notifications. Located at [`ghost/core/core/server/services/mail/ghost-mailer.js`](https://github.com/TryGhost/Ghost/blob/main/ghost/core/core/server/services/mail/ghost-mailer.js), it creates nodemailer transports based on configuration and sends immediate, single-recipient messages without batching logic.