GH

Ghost

Professional publishing platform for blogs and newsletters

Content Management Systems ★ 55.4k stars Medium setup MIT

Ghost is a powerful open-source publishing platform built on Node.js, designed for professional bloggers, publications, and creators. It includes native membership, subscription, and email newsletter features alongside a fast, modern editor.

Key features

  • Built-in membership and paid subscriptions
  • Native email newsletters
  • Fast modern editor
  • SEO-optimized by default

Pros & cons

Strengths

  • Excellent for newsletters and memberships
  • Clean, fast publishing experience

Trade-offs

  • Plugin ecosystem smaller than WordPress
  • MySQL recommended for production

Ghost replaces

Last reviewed Aug 26, 2026 · 956 words

Substack keeps 10% of every paid subscription. Ghost(Pro) starts at about $9 a month. Self-hosted Ghost keeps 0%, and costs a $5 VPS plus Stripe's processing fee. That arithmetic is the reason to run it yourself, and three facts follow immediately: production Ghost requires MySQL 8, newsletters go out through Mailgun and nothing else, and the WordPress plugin culture does not exist here. If you write, sell memberships, and want to own your list, Ghost is the best tool in the CMS category. If you need a shop, a forum, or 40 plugins, it is the wrong one.

MySQL 8 is a requirement, not a recommendation

The catalogue phrases it as "MySQL recommended for production". Ghost's own documentation is firmer: since Ghost 5, MySQL 8 is the only supported production database, and SQLite works only when NODE_ENV=development. The distinction matters when you file a bug, because the first reply will ask which database you run. So the compose file has two services from day one:

services:
  ghost:
    image: ghost:alpine
    ports:
      - "2368:2368"
    environment:
      url: https://blog.example.com
      database__client: mysql
      database__connection__host: db
      database__connection__user: ghost
      database__connection__password: ghostpass
      database__connection__database: ghost
      mail__transport: SMTP
      mail__options__host: smtp.example.com
      mail__options__port: 587
      mail__options__auth__user: [email protected]
      mail__options__auth__pass: secret
    volumes:
      - ./content:/var/lib/ghost/content
    depends_on:
      - db
    restart: unless-stopped
  db:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: rootpass
      MYSQL_DATABASE: ghost
      MYSQL_USER: ghost
      MYSQL_PASSWORD: ghostpass
    volumes:
      - ./mysql:/var/lib/mysql
    restart: unless-stopped

Pin ghost:alpine to a major-version tag before deploying, and change every password. The url value has to be the exact public address including https://, or the admin at /ghost/ loops on redirect behind a reverse proxy. The double-underscore keys are Ghost's way of expressing nested JSON config as environment variables; anything in config.production.json can be set this way. The first visit to /ghost/ creates the owner account, and 1 GB of RAM (the catalogue figure) is comfortable for a site doing tens of thousands of views a day.

Transactional mail is SMTP; newsletters are Mailgun, full stop

Ghost has two mail paths and they do not share a setting. Signup links, password resets, and staff invites go through whatever SMTP you put in mail__*; any provider works, and so does a Postfix relay. Bulk newsletters are different: the sending engine is built for Mailgun's batch API, and you configure it in Settings under Email newsletter with a Mailgun domain and API key. There is no supported way to swap in another bulk provider. Mailgun's pay-as-you-go tier is cheap at a few thousand subscribers, and a sending domain with SPF and DKIM set up takes 20 minutes. Do not be tempted to run the bulk mail through your own mail server; the honest take on self-hosted email explains why 2,000 identical messages from a fresh IP end in spam folders.

Memberships and Stripe: 0% platform fee, one connection

Settings, Membership, Connect with Stripe walks you through Stripe Connect, after which you define free and paid tiers, monthly and yearly prices, and Ghost registers the webhooks with Stripe on its own. Two requirements trip people: the site must be publicly reachable over HTTPS so Stripe can deliver webhook events, and the url must be final, because changing it later means reconnecting. Members log in by magic link (that is the transactional mail path above), and the portal, paywalled sections, and comments all come built in. On Ghost(Pro) this is the same code; the only difference on your own server is that the fee column reads zero.

Themes replace plugins, and Code Injection replaces most of the rest

A Ghost site is a Handlebars theme uploaded as a zip. The bundled theme is called Source, the theme marketplace has a few dozen free and paid options, and editing a theme is a text editor and zip -r. What WordPress does with plugins, Ghost does with three things: theme code, Settings under Code Injection (site-wide header and footer snippets, where a Plausible tag or a font import goes), and Integrations, which issue Content API and Admin API keys plus outbound webhooks. The catalogue's "smaller plugin ecosystem" con is polite; there is no plugin ecosystem, by design, and the site is faster and less hackable for it. If your publication needs event calendars, LMS features, or WooCommerce, WordPress remains the right call.

Upgrades are a tag bump if you back up first

Everything mutable is in ./content (images, themes, settings, any SQLite file) and the MySQL volume. Back up both, bump the image tag, docker compose pull && docker compose up -d, and Ghost runs its schema migrations on start. Across a major version, go to the last release of the current major first, then jump; the migrations assume that path. Ghost is 13 years old, MIT-licensed, funded by its own hosting business rather than venture money, and releases weekly, so a monthly upgrade habit keeps you close enough.

What I'd do

The compose file above on a $5 to $10 VPS with 2 GB of RAM, Caddy in front for TLS, a Mailgun domain configured the same afternoon if newsletters are the plan, and Stripe connected once the domain is final. Nightly mysqldump plus a tarball of content/, shipped off the box. Then write. A single author with 1,000 paid subscribers at $5 a month grosses $60,000 a year; after Stripe's roughly 2.9% plus 30 cents per charge that is about $54,600 on self-hosted Ghost against about $48,600 on Substack, and the $6,000 gap pays for the VPS about 50 times over.

Compare Ghost

21 head-to-head comparisons.

Similar content management systems apps