Ghost 6.0.4 upgrade broke admin login..?

Hi Ghost Forum, I’m hoping you can help me solve a rather odd and hard-to-trace bug that I seem to have found with a Ghost 6.x upgrade. (I’m a full-stack web dev and happy to help debug in detail.) Any insights would be very helpful. Thanks!


Issue Summary

  • After upgrading to 6.0.4, including the related node upgrade, I’m no longer able to log into the Ghost /admin/ panel.
  • On the Ghost admin login page, using a previously-known-good-and-working user/password login combo, I get an error, (with the big red “Retry” button) with a very cryptic error description, (please read below for the details.)
  • The error text seems to indicate an SSL problem, but I’ve double checked the SSL certificates from before-and-after the upgrade and nothing has changed with them. (They are LetsEncrypt / certbot certs, and up until this 6.x upgrade, I’ve had absolutely no issues with them.)
  • On one computer, (running macOS and Safari) with a previously-logged-in session, the Ghost admin panel is still available as usual.
  • On all other computers and browsers, the login error appears.
  • The Ghost website (i.e. the regular front-end I’m hosting) is still accessible and live to the public.

Steps to Reproduce

  1. Upgraded from 5.81.x to 6.0.4.
  2. Ran ghost doctor and no had obvious problems pointed out by the script.
  3. Checked over my config.production.jsonfile, thinking there might be a Mailgun (TLS) issue, but I haven’t find errors from there.

Setup information

Ghost Version
6.0.4, ghost-cli version 1.28.3

Node.js Version
22.18.0

How did you install Ghost?
Originally installed via DigitalOcean “one-click deployment” script, on a DO VM. Upgraded from 5.x to 6.x via ghost-mgr: ghost upgradein terminal on VM.

Provide details of your host & operating system
Ubuntu 22.04.5 LTS

Database type
MySQL 8 (? – Sorry, I can’t confirm via sudo mysqladmin version, and don’t want to possibly break permissions by forcing access to it. My version is whatever shipped with “one-click-deployment” 5.x installations.)

Browser & OS version
Chrome 139.0.x

Relevant log / error output

Shown below the Ghost admin login form, i.e. below the “Retry” button:
Failed to send email. Reason: 8078E587427F0000:error:0A00010B:SSL routines:ssl3_get_record:wrong version number:../deps/openssl/openssl/ssl/record/ssl3_record.c:354: .

Last log entry from ghost_mgr/.ghost/logs:

at Promise._settlePromise (/usr/lib/node_modules/ghost-cli/node_modules/bluebird/js/release/promise.js:604:18)at Promise._settlePromise0 (/usr/lib/node_modules/ghost-cli/node_modules/bluebird/js/release/promise.js:649:10)at Promise._settlePromises (/usr/lib/node_modules/ghost-cli/node_modules/bluebird/js/release/promise.js:729:18)at _drainQueueStep (/usr/lib/node_modules/ghost-cli/node_modules/bluebird/js/release/async.js:93:12)at _drainQueue (/usr/lib/node_modules/ghost-cli/node_modules/bluebird/js/release/async.js:86:9)at Async._drainQueues (/usr/lib/node_modules/ghost-cli/node_modules/bluebird/js/release/async.js:102:5)at Async.drainQueues (/usr/lib/node_modules/ghost-cli/node_modules/bluebird/js/release/async.js:15:14)at process.processImmediate (node:internal/timers:485:21)Code: EACCESPath: /var/www/ghost/config.production.json

Here’s myghost doctor output:

:check_mark: Checking system Node.js version - found v22.18.0
:check_mark: Checking logged in user
:check_mark: Ensuring user is not logged in as ghost user
:check_mark: Checking if logged in user is directory owner
:check_mark: Checking current folder permissions
:check_mark: Checking system compatibility
:check_mark: Checking for a MySQL installation
sudo systemctl is-active ghost_{website, actual TLD redacted}-com
Instance is currently running
:information_source: Validating config [skipped]
:check_mark: Checking folder permissions
:check_mark: Checking file permissions
:check_mark: Checking content folder ownership
:check_mark: Checking memory availability
:check_mark: Checking binary dependencies
:check_mark: Checking free space
:check_mark: Checking systemd unit file
:check_mark: Checking systemd node version - found v22.18.0`

config.production.json contents:

{
  "url": "https://{redacted}.com",
  "server": {
    "port": {redacted},
    "host": "127.0.0.1"
  },
  "database": {
    "client": "mysql",
    "connection": {
      "host": "127.0.0.1",
      "user": "ghost-{redacted}",
      "password": "{redacted}",
      "port": 3306,
      "database": "ghost_production"
    }
  },
  "mail": {
    "transport": "SMTP",
    "from": "{redacted}",
    "options": {
      "service": "Mailgun",
      "host": "smtp.mailgun.org",
      "port": 587,
      "requireTLS": true,
      "auth": {
        "user": "postmaster@mail.{redacted}.com",
        "pass": "{redacted}"
      }
    }
  },
  "logging": {
    "transports": [
      "file",
      "stdout"
    ]
  },
  "process": "systemd",
  "paths": {
    "contentPath": "/var/www/ghost/content"
  },
  "security": {
    "staffDeviceVerification": true
  }
}

Ghost introduced device verification and 2FA back in April:

This requires a working mail configuration.

The error message indicates that there is a mismatch in SSL versions. I have ran into this before with a weird VPS setup, though I am surprised this happens on DigitalOcean.

Quick workaround: set mail.options.tls.rejectUnauthorized to false in your configuration.

Better workaround: use the Mailgun API for transactional emails. DigitalOcean is starting to block standard SMTP ports, so sooner or later you probably won’t be able to send via SMTP after all.

Here’s how you can set that up (unfortunately not in the official docs):

As an alternative: you can also disable the device verification by setting security.staffDeviceVerification to false, though this does not solve the underlying issue with your transactional emails failing.

1 Like

Thanks @jannis for your quick reply!

I’ve updated my config.production.json to switch to the Mailgun API, rather than SMTP, but even after following the various forum posts related to the one you linked, I’m still having some odd feedback when I try to run ghost doctor after the config change.

Here’s my updated config.production.json:

{
  "url": "https://{redacted-tld}",
  "server": {
    "port": {redacted},
    "host": "127.0.0.1"
  },
  "database": {
    "client": "mysql",
    "connection": {
      "host": "127.0.0.1",
      "user": "{redacted}",
      "password": "{redacted}",
      "port": {redacted},
      "database": "ghost_production"
    }
  },
  "mail": {
    "transport": "mailgun",
    "from": "{redacted}",
    "options": {
      "auth": {
        "api_key": "{redacted}",
        "domain": "{redacted}"
      },
      "host": {
        "api.mailgun.net"
      }
  },
  "logging": {
    "transports": [
      "file",
      "stdout"
    ]
  },
  "process": "systemd",
  "paths": {
    "contentPath": "/var/www/ghost/content"
  },
  "security": {
    "staffDeviceVerification": true
  }
}

Based on this updated config, when I run ghost doctor I get an error, and when I tail the last log, I see:

Debug Information:
    OS: Ubuntu, v22.04.5 LTS
    Node Version: v22.18.0
    Ghost Version: 6.0.4
    Ghost-CLI Version: 1.28.3
    Environment: production
    Command: 'ghost doctor'
An error occurred.
Message: 'Cannot read properties of undefined (reading 'client')'

Stack: TypeError: Cannot read properties of undefined (reading 'client')
    at Task.pythonSetuptoolsIsRequired [as _enabledFn] (/usr/lib/node_modules/ghost-cli/lib/commands/doctor/checks/python-setuptools.js:68:52)
    at Task.check (/usr/lib/node_modules/ghost-cli/node_modules/listr/lib/task.js:67:27)
    at Listr._checkAll (/usr/lib/node_modules/ghost-cli/node_modules/listr/index.js:55:9)
    at Listr.run (/usr/lib/node_modules/ghost-cli/node_modules/listr/index.js:92:8)
    at UI.listr (/usr/lib/node_modules/ghost-cli/lib/ui/index.js:219:50)
    at /usr/lib/node_modules/ghost-cli/lib/commands/doctor/index.js:52:28
    at async DoctorCommand._run (/usr/lib/node_modules/ghost-cli/lib/command.js:207:13)

Any thoughts or insights on these errors?

Thanks again!

This is how Mailgun configuration must added.

  "mail": {
    "transport": "SMTP",
    "options": {
      "service": "Mailgun",
      "host": "smtp.mailgun.org",
      "port": 587,
      "secure": false,
      "auth": {
        "user": "mailAddress",
        "pass": "apiKey"
      }
    }
  },

Change database configuration as follows.

  "database": {
    "client": "mysql",
    "connection": {
      "host": "localhost",
      "user": "user",
      "password": "password",
      "database": "ghost_production"
    }
  },

Thanks for the suggestion @thimiraonline !

I updated my config.production.json with those, and tried ghost restart but when I tail the latest log/error, I see the same (client) error as I outlined in my previous reply here.

Upon further investigation of the (client) error, it seems like there might be an SSL configuration error that’s preventing me from accessing MySQL in my VM’s terminal via the root account:

root@host~: mysql
ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: YES)

Is there any possibility that the 5.x to 6.x upgrade could have messed up the (localhost) SSL certificate validation?

After rebooting my VM, my Ghost site is no longer publicly available, I suspect due to this (client) error.

I have full backups from my Ghost 5.x installation, and worst-case-scenario I could always rebuild the VM from scratch, but this would obviously require going through the usual (initial setup) steps with LetsEncrypt, the related DNS records, Mailgun setup, etc. and I’m hoping to avoid that!

Any further suggestions would be greatly appreciated.

Thanks.

If it were me I’d probably restore a backup from right before you updated, and then try again, but instead of updating directly from 5.81.x to 6.0.4, I’d update to the latest 5.x branch by running:

ghost update 5.130.4

Then make sure everything is working on that version. If yes, then go ahead and update to 6.0.4 (doing the node upgrade beforehand).

About your original error “Failed to send email” because of wrong SSL version, I think I got that too (can’t remember the exact error) but I had to change my SMTP port from 587 to 465, restart ghost, and then it worked for some reason.

I’d prefer to use 587 but I didn’t further investigate and just left it at 465. I’m also using Mailgun’s SMTP server but I’m not on DO. I read DO started blocking both 587 and 465 back in March. Have you been able to send transactional email from your Ghost site since March?

Anyway the mail portion of my config file looks like this now:

  "mail": {
    "transport": "SMTP",
    "from": "user@mydomain.com",
    "options": {
      "service": "Mailgun",
      "host": "smtp.eu.mailgun.org",
      "port": 465,
      "secure": true,
      "auth": {
        "user": "user@mydomain.com",
        "pass": "redacted"
      }
    }
  },

Maybe that will help.

Yes this is correct. We have to setup sendgrid API from DigitalOceans end and integrate it with our DO instance.

root@host you cannot manage Ghost Installation with remaining root user. You have to login to the ghost user.

You need to run sudo -i -u ghost-mgr and run ghost setup. Then you will be able to restore database connection and restore the SSL.

Thanks @thimiraonline - I’ve been running the ghost commands as ghost-mgr this whole time.

I just ran ghost setup and was prompted to provide the MySQL setup info (user, password etc) which was surprising to see! The ghost setup script skipped the nginx, SSL, and systemd steps, which made sense to me.

The last step of sudo systemctl start ghost_tld got stuck.

I strongly suspect something in the (migration from the latest updated 5.81.x to 6.0.4) screwed up the MySQL permissions at the Ubuntu user permissions level. (It’s recent enough that I could probably find it in the log files, but would be combing through them for much longer than it takes to rebuild the VM.)

I think I’ll just have to rebuild the VM and Ghost installation on the 5.81.x base until the Ghost team has had a chance to investigate the 6.x upgrade issues.

Thanks for your help!

I highly doubt that. The Ghost CLI sets things up initially, but an update doesn’t touch the database at all. All migrations within the database happen from within Ghost’s node.js application.

If you have automatic updates on your server this could have changed some things, but the Ghost CLI doesn’t interfere with database permissions.

The issue you’re having with ghost doctor is pretty trivial, in fact:

This is not valid JSON.

It should be:

      "host": "api.mailgun.net"

The syntax error means that Ghost cannot read config.database from the database and is therefore failing. So, classic red herring.

I’d suspect that fixing that syntax error will prompt Ghost to start properly again.

That is also not quite correct. The SMTP method is one option how the Mailgun configuration can be added. The method I linked to (Mailgun API, rather than SMTP) is better, since SMTP ports are often blocked by server providers. That exact configuration is running on nearly 800 sites on Magic Pages, so uhh…it’s valid :wink:

Following up on this thread; I’ve started up a ‘fresh’ DO VM and tried to rebuild from a base Ubuntu 24.04 LTS image, along with a Ghost 6.0.6 installation.

I’ve followed all the usual setup steps in How To Install Ghost On Ubuntu - Ghost Developer Docs – but when I try to ghost import backup-from-2025- I just get: Killed :persevering_face:

So I’ve started a fresh 24.04 VM and proceeded through all the steps, but aiming for Ghost 5 instead. When it came time to ghost install, I ran ghost install 5.130.4and I successfully stepped through the usual setup (text entry) steps, and deferred SSL setup.

I tried to ghost import backup-from-2025… but once again, I got Killed ! :sob:

How could I possibly start afresh if I can’t even restore my most-recent 5.81.x backup?

Thanks for your insights.

Do you have enough memory? I’ve seen killed come up when the OS… kills… a process because it’s out of memory.

It’s on a 2GB VM, which has been my “go-to” for my various Ghost sites for years. Unless… 6.x requires 4GB or more now..?

And for what it’s worth, the backup .zip file for the site I’m trying to restore/rebuild is only about 1.9GB.

You might want to profile the import to see what memory usage looks like. Since the import 1.9GB, the import itself might be memory limited.

Sounds very unusual, but I’ll try again with another VM, this time with 4GB of RAM! Thanks for the idea!

Another followup; I’ve created a 4GB RAM 2 vCPU droplet (VM) on DigitalOcean, and copied over my Ghost backup .zip file. When I ran ghost-mgr@host: ghost import backup-from-2025…zipthis is the output I got:

ghost-mgr@host:/var/www/ghost$ ghost import backup-from-[redacted].zip

Love open source? We’re hiring JavaScript Engineers to work on Ghost full-time.
https://careers.ghost.org

A SystemError occurred.

Message: Import file not found or is not valid JSON

Debug Information:
    OS: Ubuntu, v24.04.3 LTS
    Node Version: v22.19.0
    Ghost Version: 6.0.6
    Ghost-CLI Version: 1.28.3
    Environment: production
    Command: 'ghost import backup-from-[redacted].zip'

Try running ghost doctor to check your system for known issues.

You can always refer to https://ghost.org/docs/ghost-cli/ for troubleshooting.

Evidently, the ghost import feature is having some other issues, not just a lack of RAM!

I will try this again with a fresh 4GB RAM VM, but manually install Ghost 5.x and try to ghost import on there instead.

Sigh. :pensive_face:

I just ran into the exact same A SystemError occurred. error with Ghost 5.130.4 on a 4GB droplet.

I also tried to ghost import an even older (2024) backup .zip file, and got the same error.

I’m happy to anonymize/redact my log files, if that would enable anyone to help me further…?

Last followup on this thread, to document what I did to resolve this:

I started with a fresh DigitalOcean virtual machine “droplet” - Ubuntu 24.04 LTS, dual CPU, 2GB RAM, etc. I redirected my DigitalOcean DNS settings for my TLD to the new droplet’s IP, etc. (Since the nameservers are the same, it was an instant DNS resolution change, without needing to wait ~24hrs for DNS propagation.)

I started by enabling ufw and ran ufw allow ssh and ufw enable to continue to work via SSH, but otherwise lock down the droplet.

I worked through the usual Ubuntu setup guide here. Since I’m using Ubuntu’s ufw firewall, I had to run, sudo ufw allow ‘Nginx Full’ to allow Nginx and the ghost setup features to work smoothly.

When it came time to install Ghost, I ran, ghost install 5.130.3and worked through the usual ghost setup steps, including the initial MySQL user/password setup, Let’s Encrypt SSL setup stages. All went OK.

I was able to register the initial user and login into the fresh installation via https on the live TLD.

Then, to restore from my previous (Ghost 5.81.x) backup from a few months ago, I scp’ed my .zip backup file to the droplet, moved it into the /var/www/ghost/content/ folder, and unzipped it via sudo unzip -o backup.zip, where the -o flag overwrites existing files/folders.

I then uploaded the custom theme, the members .csv file, and imported the previous content / posts / pages. This all went OK, and the previous backup/copy of the site was restore.

I’m sure I’ll still have to redo things like the Mailgun API key setup (with the related DNS TXT records etc) and probably redo the Disqus comments integration.

For now, I’m OK with being somewhat “stuck” on Ghost 5.x for this site, at least until a more robust Ghost 6.x or future upgrade can handle my site’s migration without just getting a mysterious Killed feedback in the terminal.

I’d be happy to help the Ghost development team diagnose those issues (perhaps with some more direct feedback, i.e. on Discord?) to get to the bottom of the migration issue.

Thanks to everyone here who tried to help out!