Why was I ghosted at my potluck?

I organized a potluck, but hardly anyone showed up. Ghosted.

I use Ghost to organizing self-throwing parties like this: Twice a year I spend an afternoon scheduling dozen emails to out: Six potluck announcement emails. Then I use the duplicate post feature and set up six more reminder emails.

Then I just show up with my Instant Pot and there are people there. It’s pretty great.

But last month I started to suspect something was off when a couple people asked if the event was still happening. The day before the event I checked Ghost and found that both the original post and reminder post had failed to send. there was not specific error message with a clue as to the problem, but the UI did offer to try again. So I sent a last-minute reminder and saw it was successful, and set some beans to soak for my bean and barley soup.

But the reminder, despite reporting success, never went out either. It would have been great if Ghost emailed me as an admin to let things know that things were broken.

A few days later, I got direct email from someone that they had tried to subscribe the group, but that wasn’t working either.

Clearly something has gone wrong with Mailgun. And already one lesson has been learned: By using Mailgun for both bulk and transactional email, all my email eggs are in one basket. If bulk email becomes broken, Ghost couldn’t email me with a transactional email to tell me there was a problem if it wanted to.

Here I’m going to try to try answer my own question by reviewing my logs and checking Mailgun. I’ll report back what I find and recommend how to improve the situation so you don’t get ghosted at your own self-throwing parties.

1 Like

Summary: The problem was that I host Ghost at home without a static IP, while also setting up the Mailgun security feature to allow sending from specific IP addresses. So you can guess what happened… My IP address changed. So the fix was to go into the Mailgun settings to update my IP address. I /could/ disable the IP allowlist feature, but I like the security benefit and hopefully will have learned to update this detail the next time my IP changes.

The other thing I learned is that now Mailgun requires that each new API key be assigned a Role. But Ghost does not yet appear to document which role should be selected as the minimum one that Ghost needs.

Also, Ghost could help self-hosters avoid SMTP problems by pre-installing nodemailer-mailgun-transport - npm in the Docker container and in the default CLI install. Then it would be possible to configure this “transport”, bypassing all the problems with different platforms blocking different SMTP ports.

What follows are my investigation notes for those who might be interested.


The first thing I did was to check Mailgun. I don’t see any clear warnings in that account of any problems. My account appears to be in good standing.

Next, I checked the logs. There is plenty of evidence of email problems there. Here’s a sample:

ERROR [BULK_EMAIL_DB_RETRY] Sending email batch 68178f195718ef0001f0b91f  - Failed (1st try)
ERROR [BULK_EMAIL_DB_RETRY] Sending email batch 68178f195718ef0001f0b91f  - Failed (2th try)
ERROR [BULK_EMAIL_DB_RETRY] Sending email batch 68178f195718ef0001f0b91f  - Failed (3th try)
ERROR [BULK_EMAIL_DB_RETRY] Sending email batch 68178f195718ef0001f0b91f  - Failed (4th try)
ERROR [BULK_EMAIL_DB_RETRY] Sending email batch 68178f195718ef0001f0b91f  - Failed (5th try)
ERROR [BULK_EMAIL_DB_RETRY] Sending email batch 6827612e5718ef0001f0b972  - Failed (1st try)
ERROR [BULK_EMAIL_DB_RETRY] Sending email batch 6827612e5718ef0001f0b972  - Failed (2th try)
ERROR [BULK_EMAIL_DB_RETRY] Sending email batch 6827612e5718ef0001f0b972  - Failed (3th try)
ERROR [BULK_EMAIL_DB_RETRY] Sending email batch 6827612e5718ef0001f0b972  - Failed (4th try)
ERROR [BULK_EMAIL_DB_RETRY] Sending email batch 68178f195718ef0001f0b91f  - Failed (6th try)
ERROR [BULK_EMAIL_DB_RETRY] Sending email batch 6827612e5718ef0001f0b972  - Failed (5th try)
ERROR [BULK_EMAIL_DB_RETRY] Sending email batch 6827612e5718ef0001f0b972  - Failed (6th try)
ERROR [BULK_EMAIL_DB_RETRY] Sending email batch 68178f195718ef0001f0b91f  - Failed and stopped retrying: max retries reached
ERROR Error sending email 68178f195718ef0001f0b91e
ERROR Failed to send email. Reason: Invalid login: 535 Authentication failed.
ERROR "POST /members/api/send-magic-link/" 500 270ms
ERROR Unauthorized
ERROR [EmailAnalytics] Error while fetching
ERROR Unauthorized
INFO [EmailAnalytics] No new events found
ERROR Error while fetching email analytics Unauthorized
ERROR Unauthorized
WARN Missing mail.from config, falling back to a generated email address. Please update your config file and set a valid from address
"Mailgun Error 401: Forbidden"
"https://ghost.org/docs/newsletters/#bulk-email-configuration"

Error ID:
    0af28be0-3385-11f0-b175-8fdf49042402

Error Code: 
    BULK_EMAIL_DB_RETRY

Details:
    {"error":{"status":401,"message":"Unauthorized","details":"Forbidden","type":"MailgunAPIError"}

OK, I get it: Everything points to unauthorized/forbidden errors, and there’s a warning about setting my “mail.from”

Before I go further, let’s see if I can easily trigger this by trying to sign up an account.

Trying to sign up, now I get “Failed to sign up, please try again” as a user. In the logs, I see:

ERROR Failed to send email. Reason: Invalid login: 535 Authentication failed.
Failed to send email. Reason: Invalid login: 535 Authentication failed.
"Please see https://ghost.org/docs/config/#mail for instructions on configuring email."
Error ID: 948180a0-3f1a-11f0-b175-8fdf49042402
Error Code:  EAUTH

So clearly my authentication with Mailgun became completely broken somehow.

In my Ghost admin area, it still says that Mailgun is setup, but let’s try regenerating an API key.

Now, this is interesting. In the Mailgun API key area, I see my original key from 2022. Then I see this new API key that I didn’t create with this name:

“Replacement key for legacy encrypted key, converted on May 19 21:05:28 UTC 2025”.

I see it has a role of “None”.

That would be around the time that my setup broken.

It’s not clear what Role that Ghost requires for Mailgun keys.

I generated a new API key with a role of “Admin” and added that to Ghost, but I still a sign-up error. But I can see the sign-up error is using SMTP, and thus the transactional email configuration, which is confusingly different from the main config. So now I’m reviewing my SMTP settings.

I’m contemplating if I try use the “nodemailer-mailgun-transport” ( nodemailer-mailgun-transport - npm ), but it might be a challenge to mount that extra node module into the docker container, so I’m not doing that now. (But hmm, it’s be a nice bonus if the container included that by default.)

Mailgun’s AI say:

If you are encountering authentication errors with the SMTP interface and the API, it might be related to changes made in April 2024. Specifically, if your IP allowlist contained IPs and/or IP ranges prior to April 2024, you may experience errors when sending messages. Mailgun issues a 535 Authentication failed error in the SMTP session and a 401 Forbidden error for HTTP API responses. These errors might be displayed differently by various email applications or clients.

The bit about April 2024 turned not to be related, but it did make me realize what the problem was. About a month ago, the IP address of my home server changed… it’s not static. I never put the new address into Mailgun. Therefore, Mailgun was blocking my requests because I hadn’t yet added my new/current IP address to the list of allowed sending IP addresses.

Ghost does natively include nodemailer-mailgun-transport for transactional email (ref), but it’s not in the docs!

Here’s the related thread:

Thanks for highlighting that. As a forum moderator yourself, you have seen that a lot of new users struggle with Ghost having two different ways to configure email. A better user experience would be this:

  • If the Mailgun API is provided and no transactional email config has been detected, default to using Mailgun for transactional emails too.

At that time, the docs can advise that for safety it’s a good idea to use a separate service for transactional emails in case there’s a temporary problem with Mailgun.

The second-best option to improve UX is to document an example of of manually setting up nodemailer-mailgun-transport. But that’s also confusing-- if users can paste their API key into the admin interface, why should they need to paste it a second time into the config files?

As a start, I’ve documented the support of the mailgun transport at the node module level.

This also documents that only a select list of transactional email services are supported, not all the transports that Nodemailer supports.

1 Like