Getting Ghost to send emails using Postfix/Sendmail in 2023

A description of my battle with, and eventual victory over, Ghost and Postfix to get them to play nicely together.

An image of a number of emails flying out of a laptop, overlaid onto a map of the world.
Image by Muhammad Ribkhan from Pixabay

My first post on this blog and it's about ... getting Ghost to actually work on my server! The instructions in this blog apply to both v5.70.1 and v5.71.2, as I upgraded in the middle of resolving the issue. To anyone reading in the future, they likely work on your version too, but your mileage may very.

As some background: I was having a real nightmare of a time trying to get Ghost to send functional emails (i.e. for inviting people as staff, password resets, etc) when locally installed. As a clarifying remark: this guide won't help you with mass emails (i.e. newsletters) and you're better off using something akin to Mailgun for this.

I have my server set up such that all of my mail is forwarded, using Postfix as a Mail Transfer Agent, to Office 365. I have all of the SPF, DKIM and DMARC records set at domain-level such that email emerging from Office 365 should be received and not marked as spam by the recipient's email service.

By default, Ghost uses Direct Send via the Nodemailer dependency that is installed alongside it. This seemed to work fine for emailing within my domain boundary (i.e. from Ghost to my main @giles.tech email address) but immediately fell over as soon as I wanted to invite my wife to act as 'editor' such that these blog posts are actually readable. This didn't work when emailed to her Gmail account nor when I was testing with my own Gmail address. Ghost simply would not send the email and wrote an error to the logs.

The main symptom is that the modal below will appear on screen for up to a minute:

Frozen modal example

The specific error I was getting within the production.error.log was as follows:

Error sending email: Failed to send email.
Reason: Email has been temporarily rejected.
Please check your email settings and resend the invitation.

To complicate matters further, I found that there is a lot of historic documentation on the internet about how to configure Ghost using the config.production.json file, but very little guidance in terms of what are actually valid options for the 'mail' section of this configuration. In fact the main solution within the relevant documentation is to simply register for and configure Mailgun, and then to point Ghost at Mailgun. This seemed silly given I am already paying for Office 365 and have the rest of the server routing mail through it very happily.

Unsatisfied with the "go sign up for something new" answer, I did some digging and eventually came across this significant help guide from ZenSoft that provides a walk through of setting up Sendmail & Postfix to use Gmail.

Installing Sendmail/Postfix on Ubuntu and configuring your Ghost blog to use it
If you are using one of the “recommended” email providers such as Mailgun you should be fine following the official setup guide. Unfortunately, if you are hosting your email elsewhere (or, God forbid, running your own MTA), then Ghost can quickly let you down. For me, I have my mail

This is some good advice and actually a reasonably concise description of all the steps it took to get Postfix working, but the relevant step was Step 6 which details the following changes which need to be made to the config.production.json file:

mail: {
    "from": "Ghost <ghost@giles.tech>",
    "transport": "SMTP",
    "options": {
      "service": "sendmail",
    }
  },

Line 2 is probably unnecessary - I know in my configuration this gets overridden anyway by part of the Postfix configuration to ensure that all email originating from my server is rewritten to no-reply@giles.tech with a few exceptions.

The meat is really changing the transport to SMTP and adding the service of sendmail. I've just tested leaving the "transport":"Direct" and this causes an immediate failure when I reboot Ghost, so both lines are relevant.

Once you've updated the file, switch to your Ghost user (as required by the set-up instructions[1]) and run ghost restart. I had some errors with my JSON (a stray comma!) which was easy to fix.

Once my Ghost admin panel was responsive again, I went to invite my wife and the modal disappeared immediately, letting me know it had been successful. My mail logs also showed this email being passed on to Office 365 for onward sending.

Hopefully this helps somebody else in the same boat.


  1. https://ghost.org/docs/install/ubuntu/ under Server Setup -> Create a new user. ↩︎