A fresh self-hosted Ghost cannot reliably send anything: staff invitations, password resets and the magic links members use to sign in all fail or land in spam until mail is configured. Ghost sends through Nodemailer, so pointing it at Postwing is a few lines in its config file.
| Setting | Value |
|---|---|
| SMTP host | smtp.postwing.app |
| Port | 587 |
| Encryption | STARTTLS (the connection is upgraded to TLS before login) |
| Username | The login of an SMTP token for your domain |
| Password | The password of that token — shown once, when the token is created |
Open config.production.json in your Ghost install directory and replace the mail block:
"mail": {
"transport": "SMTP",
"from": "'Acme' <noreply@your-domain.com>",
"options": {
"host": "smtp.postwing.app",
"port": 587,
"secure": false,
"auth": {
"user": "token-login@your-domain.com",
"pass": "your-token-password"
}
}
}| Key | Value |
|---|---|
transport | SMTP |
from | An address on your verified domain, optionally with a display name |
options.host | smtp.postwing.app |
options.port | 587 |
options.secure | false — STARTTLS is negotiated automatically |
options.auth.user | The login of an SMTP token for your domain |
options.auth.pass | That token's password |
For port 465, set secure to true:
"options": {
"host": "smtp.postwing.app",
"port": 465,
"secure": true,
"auth": { "user": "token-login@your-domain.com", "pass": "your-token-password" }
}Ghost reads its config only at startup, so restart it:
cd /var/www/ghost # your Ghost install directory
ghost restartEvery config key can be set as an environment variable, with nested keys joined by a double underscore:
# docker-compose.yml — Ghost reads nested config from double-underscore variables
services:
ghost:
image: ghost:6
environment:
url: https://blog.your-domain.com
mail__transport: SMTP
mail__from: "'Acme' <noreply@your-domain.com>"
mail__options__host: smtp.postwing.app
mail__options__port: 587
mail__options__auth__user: token-login@your-domain.com
mail__options__auth__pass: your-token-password Ghost has no test button. Invite a staff user from Settings → Staff, or sign up as a member on your site through the Portal. If the email does not arrive, ghost log shows the SMTP server's reply.
| Error | Cause and fix |
|---|---|
Failed to send magic link email | SMTP rejected or unreachable. Check ghost log for the exact reply. |
| Changes have no effect | Ghost was not restarted, or you edited config.development.json. |
| Connection timeout | Outbound port blocked. Use 8587 with secure: false, or 8465 with secure: true. |
| TLS handshake error | secure does not match the port — false for 587, true for 465. |
535 authentication failed | Wrong token login or password. |
Mail sent from noreply@ a subdomain | mail.from is missing. Set it to an address on your verified domain. |
| Newsletter will not send | Expected — newsletters need the Mailgun integration, not SMTP. |
No. SMTP in Ghost is for transactional email only — staff invitations, password resets, member sign-up and sign-in links. Newsletters go through Ghost's bulk email integration, and self-hosted Ghost supports Mailgun only for that. There is no setting that routes newsletters over SMTP.
In the mail block of config.production.json in your Ghost install directory, not in Ghost Admin. The Email newsletter settings in Admin are for the bulk Mailgun integration and do not affect transactional mail. After editing the file, run ghost restart.
The member sign-in or sign-up email could not be handed to the SMTP server. Check the mail block for a typo, a wrong port and secure pair, or wrong token credentials, then look at ghost log for the server's actual reply. Ghost does not pick up config changes until it is restarted.
false with port 587 — the connection starts in plaintext and is upgraded with STARTTLS before the password is sent. true with port 465, where TLS starts immediately. true on 587 or false on 465 fails with a handshake error or a timeout.
The value of mail.from. If it is missing, Ghost falls back to a noreply address on the site's own hostname, which is often a subdomain you never verified for sending. Set mail.from explicitly to an address on your verified domain so DKIM, SPF and DMARC align.
No. Ghost(Pro) runs mail for you and does not expose config.production.json. This guide applies to self-hosted Ghost, whether installed with Ghost-CLI or run in Docker.