Blog Migration to Ghost
I may have noticed, that the design of the blog has changed dramatically. It is because I've migrated to Ghost. It is a self-hosted blogging/monetization platform, like a self-hosted medium or a substack in a lot of ways. They offer (almost) all the same features in the self-hosted version, as they do in the SaaS version, which is always great to see...
(As a side note, I hate it when a self-hosted option has stuff like OIDC SSO behind a paywall, it's so moronic).
I've tried it out using an LXC container with the Proxmox VE Scripts, but ultimately decided to put it into docker instead.
Originally, the blog was hosted using a self-hosted instance of WriteFreely. It is a very nice platform if you focus primarily on text. One of the drawbacks I had with it, was that any time I needed to add an image to the blog post, I would have to first upload it somewhere, and the youtube embeds for the podcast episodes would have to be formatted manually via an <iframe/>
and they would look like ass on mobile.
Also, this migration has given me a chance to repatriate the blog to my homelab, as it's not as critical, and I want to reduce my VPS portfolio.
I've used portainer stacks to deploy this app, and it was fairly straight-forward.
I took the docker compose file from the documentation, tweaked it slightly, and simply created a new stack.
services:
ghost:
image: ghost:5-alpine
restart: always
ports:
- 8080:2368
environment:
# see https://ghost.org/docs/config/#configuration-options
url: https://blog.vasi.li/
database__client: mysql
database__connection__host: db
database__connection__user: root
database__connection__password: $MYSQL_ROOT_PASSWORD
database__connection__database: ghost
# mail settings
mail__from: $EMAIL_FROM
mail__transport: SMTP
mail__options__host: $EMAIL_SERVER
mail__options__port: 587
mail__options__service: Exceede Media
mail__options__auth__user: $EMAIL_USER
mail__options__auth__pass: $EMAIL_PASSWORD
volumes:
- ghost:/var/lib/ghost/content
db:
image: mysql:8.0
restart: always
environment:
MYSQL_ROOT_PASSWORD: $MYSQL_ROOT_PASSWORD
volumes:
- db:/var/lib/mysql
volumes:
ghost:
db:
The homelab is already set up with traefik
for https fronting, so adding the domain mapping and re-pointing DNS was a matter of few minutes.
What did take considerably longer was migrating old content. Unfortunately there's no native way to migrate from WriteFreely.
I spend several days playing with lexical
, which is Meta's WYSIWYG editor, which ghost uses internally. I tried going the route of dumping the database, which contains all posts in markdown
format, then using marked
library, which can output AST for the content, and then try to use lexical
API to convert it into something ghost would be happy with, however I couldn't really get it to work.lexical
is very "build it yourself" in terms of what is renderable, and trying to use ghost's lexical package was incompatible with main lexical
package, and in the end I kinda gave up and converted all the posts by hand 😢.
I was also able to wire up Mermaid, by putting this small script into the template override:
<script type="module">
import mermaid from "https://unpkg.com/mermaid@11.6.0/dist/mermaid.esm.min.mjs";
mermaid.initialize({startOnLoad:true});
await mermaid.run({
querySelector: 'code.language-mermaid',
});
</script>
```mermaid
pragma
Ghost also allows paid tiers, but considering the amount of subscribers I have it's a bit premature 🤣