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.