🚀 Ghost v6 Upgrade + Docker Migration: What I Learned (So You Don’t Have To)
I’ve just finished upgrading my blog to Ghost v6 and finally moved it fully into Docker, and a few things caught me off guard that might save other self-hosters some time.
1. The database change - MariaDB → MySQL 8
Upgrading to v6 requires moving away from MariaDB. I handled this first on my CLI-managed Ghost install, and it worked - but only after one crucial step:
👉 Purge every MySQL/MariaDB package before installing MySQL 8.
I had leftover libraries and a MySQL meta-package that silently interfered with the upgrade. Once everything was removed and MySQL 8 was installed cleanly, the migration ran exactly as expected.
Where things went wrong:
When I tried to migrate this new MySQL 8 database into the Docker version, the importer kept failing. It complained about a missing table:
Table 'ghost.benefits' doesn't exist
No amount of repair/debugging seemed to fix it.
So…
2. Migrating to Docker without downtime
To keep my site online throughout, I ran both versions side-by-side:
- the old, CLI-based Ghost
- the new Docker stack on a different internal port
After testing uploads, routing, federation, and admin access, I switched NGINX over to the container.
Because the Docker importer refused to accept my perfectly valid MySQL 8 DB, I fell back to the reliable method:
- exported content as JSON
- exported images and media
- exported members to CSV
- imported everything cleanly into the Docker install
- reconfigured mail, storage, integrations, etc.
Not glamorous, but 100% reliable.
P.S. If you face the “migration lock” issue, Raki’s solution here worked for me!
3. Federation - the part everyone gets stuck on
Ghost’s federation is still young, so here’s what matters:
Serve JWKS locally
If you use reverse proxies or multiple domains, make sure:
/.ghost/activitypub/v1/.well-known/jwks.json
is served directly by Ghost, not intercepted by another service.
Without a working JWKS endpoint, other servers simply can’t verify you.
Resetting ActivityPub registration
The usual fix:
Settings → Network → ActivityPub → toggle off → save → toggle on
This forces Ghost to re-register with ap.ghost.org.
But sometimes it isn’t enough
In my case, ap.ghost.org had cached an old, broken registration from before I fixed my domain setup. It wouldn’t clear, and the dashboard kept erroring.
My solution:
- self-host the ActivityPub service
- point Ghost at my ActivityPub container instead of Ghost’s cloud one
- clear the cached integration entry in the database
- let Ghost register fresh with my own endpoint
Unexpected bonus:
Self-hosting ActivityPub actually made federation faster and more reliable, since everything stays within my own stack.
4. Dockerising Ghost = massive quality-of-life upgrade
Ghost was the last non-Docker service on my servers, and everything feels smoother now:
- updates are clean
- federation runs in its own container
- analytics lives in its own container
- my NGINX proxy setup is consistent
- backups are simpler
- zero system-level dependency conflicts
Honestly, I should have done this ages ago.
If anyone else is mid-upgrade or stuck in federation purgatory, feel free to ask questions - I’ve just survived the full v6 gauntlet and happy to help! 😅
Laura Hargreaves
Localisation engineer, language technologist and general tinkerer with opinions. I write about tech, localisation and life on the open web — chasing internet nostalgia and genuine connections online. Cat mum ×2. 🌍💜