Ghost 6 Upgrade: From Docker to the Fediverse
Exploring the upgrade to Ghost 6, from Docker challenges and database migrations to Fediverse integration, and why the release feels like a foundation for the future rather than a breakthrough today.

Software upgrades often carry the promise of transformation. Ghost 6 positioned itself as a milestone release, introducing native ActivityPub support, deeper analytics through Tinybird, and a refreshed administrative experience. For independent publishers, these features suggested broader reach, better insight, and smoother day-to-day management.
The appeal was strong. ActivityPub federation meant that a Ghost blog could be followed directly from Mastodon or Threads, tapping into the growing Fediverse without depending on email or search engines. Tinybird integration promised a more sophisticated view of reader behavior. Combined with performance improvements and ongoing membership features, Ghost 6 looked like the next step forward.
This article explores what Ghost 6 promised, the realities of upgrading, and whether the new features live up to expectations.
The Allure of Ghost 6
The selling points of Ghost 6 were clear. For years, Ghost has been positioned as a publishing platform that puts creators in control, and this release appeared to reinforce that mission.
The headline feature was ActivityPub support. By connecting Ghost to the Fediverse, blog posts could appear directly in Mastodon feeds, Threads timelines, and other federated platforms. In theory, this was the answer to discoverability: a way for independent publishers to reach readers without relying solely on email lists or the whims of search engine algorithms. With more platforms adopting ActivityPub, it felt like a future-proof step toward decentralized distribution.
Learn about the Fediverse
Analytics was another major attraction. Through its new Tinybird integration, Ghost promised deeper insights into traffic and engagement. Publishers could expect real-time dashboards and richer event tracking. For anyone trying to understand audience behavior, this seemed like a significant upgrade.
Beyond these flagship features, Ghost 6 also promised a cleaner administrative experience and ongoing refinements to its membership tools. Combined, the release was presented as both a technical evolution and a shift in how publishers could grow and sustain their audience.

Cathy Sarisky, a well known Ghost expert in the community, discusses upgrade experiences.
With these promises in mind, the decision to upgrade felt obvious. The question was not whether Ghost 6 would be installed, but how quickly it could be put into production.
The Reality of the Upgrade
The upgrade to Ghost 6 was not a simple pull-and-restart. Each layer of the stack introduced complications, and what began as a routine version bump turned into a series of hard lessons.
A Custom Docker Compose Deployment
The official Ghost 6 guidance targeted users moving from ghost-cli
to a clean Docker stack. Technodabbler was already running on Docker Compose, which meant the recommended compose file could not be dropped in as is. The practical approach was to start from Ghost’s compose, strip out services already present in the stack, and stitch the rest into the existing configuration. Using the include
keyword made this manageable, but only after a careful read of every service, volume, and environment block.

Merging was the easy part. The real work was alignment. Services had to attach to the correct user-defined network, depends_on
needed to reference existing service names, and healthchecks had to match the way containers were already supervised. Care also had to be taken with data volumes, since Ghost’s defaults could easily overwrite existing mounts. The environment variables required careful review as well, ensuring that database credentials and secrets matched the values already in use. In the end, the hybrid file ran cleanly, but only after a full analysis of Ghost’s compose and several small but critical changes to fit the production layout.
Migrating to MySQL 8
MariaDB had worked reliably for years, even though Ghost never officially supported it. The development team has often explained that, as a small project, they could only commit to maintaining one database system. For the most part, Ghost tolerated MariaDB without issue. That changed with Ghost 6, when the new ActivityPub feature introduced queries that simply would not run on MariaDB.
At first glance, the solution seemed simple: migrate to MySQL 8, the database Ghost was designed for. In practice, the process uncovered subtle but significant differences between the two systems. Dumps from MariaDB included schema definitions that MySQL rejected. Default values were set on certain type of columns allowed MariaDB but not on MySQL.
The only reliable strategy was to export the database then clean the resulting SQL dump manually before importing into MySQL 8. It was a tedious step, but once complete, Ghost 6 ran smoothly. The experience made clear that while MariaDB had been an acceptable workaround in the past, ActivityPub drew a firm line: Ghost now required MySQL.
Wrestling with Caddy and Redirects
Ghost 6 now runs three separate services: the main blog, the analytics endpoint, and the ActivityPub service. In Docker, each one listens on its own port, but Ghost expects them to be available under the same domain at different paths. A reverse proxy is required to bridge this gap. Cloudflare can handle TLS termination and redirects, but it cannot rewrite paths. That made Caddy essential for routing requests to the right backend service.
ActivityPub requests such as / .ghost/activitypub/
, /.well-known/webfinger
, and /.well-known/nodeinfo
had to be forwarded to the ActivityPub container. Analytics endpoints, accessed through paths like /.ghost/analytics/
, also needed to be rewritten before they reached their backend service. Without Caddy handling these routes, Ghost’s components could not function together under a single domain.

With routing in place, the second issue surfaced: the infamous infinite redirect between Cloudflare and Caddy. Cloudflare terminated TLS at the edge and forwarded plain HTTP traffic, while Ghost enforced HTTPS based on its configured URL. Because Caddy defaulted to sending X-Forwarded-Proto: http
, Ghost assumed the connection was insecure and tried to redirect back to HTTPS. That redirect hit Cloudflare, which sent it back to Caddy, and the loop continued endlessly.
Cloudflare accelerates and protects the Technodabbler site.
The solution was to commit fully to SSL across the entire chain. Caddy was configured with internal certificates and treated every connection as HTTPS, while Cloudflare’s tunnel was updated to connect securely using the correct SNI for technodabbler.com
. This ensured that Ghost always saw requests as secure, breaking the loop cleanly. In the end, running end-to-end TLS was the only approach that made sense, simplifying the setup and avoiding fragile header rewrites.
Deploying Tinybird Analytics
Another headline feature of Ghost 6 was its new analytics system, built on Tinybird. On paper, the integration promised richer insights and real-time dashboards. In practice, enabling it meant introducing several new Docker components and wiring them into the existing stack. The additional services made the deployment more complex and fragile, with more containers to supervise and more moving parts that could fail.
The real disappointment came after it was running. Despite being set up correctly, Tinybird reported a quota error within a day, even while no dashboards were in use. Quotas were supposed to apply only to interactive analytics queries, not passive data collection, which made the error all the more confusing. Worse, the data it provided did not go beyond what was already available through Plausible, which is the platform used by Technodabbler. And event without the Tinybird-powered analytics, newsletter performance and membership analytics were still available through Ghost itself, just as before.

After some trial, it became clear that a Tinybird-powered analytics solution did not offer meaningful value for a small site already running Plausible. Economically, maintaining two analytics systems made little sense, and the free tier quotas were easy to exhaust. For larger sites, or professionally hosted Ghost deployments such as those on ghost.org or magicpages.co, the approach is attractive, since the pipeline can run on a larger Tinybird instance with paid capacity.
Making the Fediverse Work
The first challenge was simply proving that Ghost’s ActivityPub integration was working at all. Early tests from Mastodon resulted in repeated 404 errors when trying to follow the blog. Debugging meant inspecting the endpoints manually, checking .well-known/webfinger
, the actor profile, and the outbox to confirm they responded correctly. The culprit turned out to be a conflict between the blog’s canonical domain and its redirect. The ActivityPub address was @[email protected]
, but the site defaulted to www.technodabbler.com. Since technodabbler.com was only set up as a redirect, federation requests were failing. The only reliable fix was to flip the configuration: make technodabbler.com the default domain, and set www.technodabbler.com as the redirect. Once this was done, Mastodon stopped returning 404 errors and the account could be followed.

With the plumbing in place, Ghost began federating content through its outbox. But the outbox only contained posts created after ActivityPub was enabled. Existing articles never appeared, and the only workaround was to unpublish and republish them, which was impractical for a large archive. Even for new content, distribution depended on already having followers on the receiving server. Without someone following from a node like mastodon.social, posts would never propagate there. Federation quickly revealed a chicken-and-egg problem: visibility required followers, but followers required visibility.

When posts did deliver, results were mixed. The first Note never appeared on Mastodon until a second one was published, and Threads federation was still only partially deployed. While the setup confirmed that Ghost could technically federate into the Fediverse, it was far from frictionless. For a site that already had a Mastodon account and used Postiz to cross-post announcements, native ActivityPub support added little beyond the novelty of having a followable blog handle.
Was It Worth It?
Upgrading to Ghost 6 was an interesting journey. The flagship features, Fediverse integration and a Tinybird-powered analytics solution, both showed promise but did not add much value for a smaller independent site already running its own tools. For now, federation feels limited, and the analytics are better suited to larger deployments.
That said, the upgrade left the platform in a stronger place. Migrating to MySQL 8, consolidating everything under SSL, and refining the Docker deployment all improved the overall setup. Day-to-day administration feels smoother, and Ghost continues to be a solid foundation. The Fediverse support in particular is only at the beginning, and as the ecosystem matures, it could become a powerful way for independent publishers to reach new audiences.
In the end, Ghost 6 was less about immediate gains and more about preparing for what comes next.