3 min read

Ghost CMS on Debian

A practical guide to running Ghost 6 on Debian 13 despite its official Ubuntu-only support, using Node 22, Percona MySQL 8, systemd, CloudPanel, nginx, and a few careful workarounds.

I haven't had much time to post. A lot has been happening in my life: losses, goodbyes, and changes (something I hate). But this morning, when I sat down in front of the PC and saw that my server had run into a small problem that prevented access to some of my sites, I fixed it right away and brought all the services back up. The last one was this site, which I stared at for about 20 minutes, frozen.

I've had a few websites built 100% by hand (and almost all of them were on this domain), and I loved it. I've always been terrible at layouts and design, which is good for me because I was constantly changing everything here on the site. It never stopped and eventually became exhausting. From complex to minimalist.

But this year, specifically this year, along with everything life threw at me, work consumed me almost completely. With that said, I needed to move to something faster to update, and since I didn't want WordPress, I chose Ghost.

I'd used Ghost before and had always liked it. I even tried other frameworks, but Ghost was by far the one that gave me the most satisfaction.

But now I had a different little problem when it came to getting this framework running, and that problem was Debian. I knew the documentation and knew Ghost had been designed for Ubuntu, but in theory it would run on Debian with one or two workarounds.

Everyone who has installed Ghost with ghost-cli knows the first frustration: the official tool only embraces Ubuntu LTS. Run ghost install on Debian and it knocks on the door, performs a stack check, sees ID=debian, and simply refuses to continue. The message is clear:

Unsupported system.

But "unsupported" doesn't mean "impossible." At its core, Ghost is a Node app with three real requirements:

  • Node 22
  • MySQL 8
  • A process manager, with the app served behind a reverse proxy with TLS

None of that is exclusive to Ubuntu.

ghost-cli only assumes Ubuntu so it can automate those pieces. If you provide each one yourself, using the proper Debian equivalent, Ghost runs smoothly.

That's exactly what I did on my server, running Debian 13 "Trixie." Here's the recipe for this little achievement.

Table of Contents

  1. Disable the Gatekeeper
  2. Node 22 from the Right Source
  3. MySQL 8 via Percona
  4. systemd Is the Part That Just Works
  5. The Split That Brings Everything Together

1. Disable the Gatekeeper

ghost-cli v1.29.3 refuses anything that isn't Ubuntu during the stack check. The key is to bypass that check and install Ghost without letting it block the process because of the operating system, while taking responsibility for the dependencies myself. From that point on, it treats the installation like a normal production instance: versioning, systemd, updates, everything.

2. Node 22 from the Right Source

Ghost 6 requires Node 22, and the Debian repository doesn't reliably provide that version. The clean solution is the official NodeSource repository at deb.nodesource.com/node_22.x. This makes /usr/bin/node exactly the 22.x version Ghost requires, with no hack and no need to compile anything.

3. MySQL 8 via Percona

This is the obstacle that defeats most people. Ghost 6 requires MySQL 8 and refuses MariaDB, while Debian offers MariaDB by default. Installing Oracle's MySQL on Debian is annoying.

The elegant way out was Percona Server 8.0, a drop-in server that is 100% protocol-compatible with MySQL 8.

4. systemd Is the Part That Just Works

This one came for free. ghost setup systemd generates a service with User=ghost, ExecStart=node ghost run, and Restart=always. Since Debian's systemd is identical to Ubuntu's, the service came up verbatim, without a single line of adjustment. It's proof that much of the "Ubuntu-only support" is more convention than technical necessity.

5. The Split That Brings Everything Together

The trick that makes all of this solid is not letting ghost-cli handle the edge. Nginx and TLS are used the way they always have been: nginx handles the reverse proxy and terminates HTTPS with a Let's Encrypt certificate, forwarding requests to Ghost at 127.0.0.1:2589. ghost-cli handles only the portable parts, the Ghost application and systemd.

The result is a clean split:

  • CloudPanel: nginx, TLS, and Percona/MySQL 8, the "Ubuntu-specific" part that causes trouble in ghost-cli
  • ghost-cli: the Ghost app and the systemd service, the OS-agnostic part

In the end, Ghost's "Ubuntu-only support" is, in practice, support for the automation, not an engineering constraint. When you break the requirement down into its real parts, each one has a legitimate replacement on Debian. The final trick is remembering to pass the stack-check bypass during updates with ghost update, and never letting ghost-cli try to touch nginx or TLS.

I think that's basically it, if memory serves.

Sorry for disappearing. I'm sorting out my life, and I'll get back to posting more.

Reply

Got a thought? Reply by email, or publish a response on your own site and it'll show up above via Webmention.

Reply by email