< the skull

GHOST CMS ON DEBIAN
===================
2026-08-09

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
-----------------

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

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.

Links:
  [1] #1-disable-the-gatekeeper
  [2] #2-node-22-from-the-right-source
  [3] #3-mysql-8-via-percona
  [4] #4-systemd-is-the-part-that-just-works
  [5] #5-the-split-that-brings-everything-together


< the skull