Setting up the blog
Which blogging platform to choose?
Choices
I could have picked:
- a free hosted solution like Medium or Blogger
- a paid solution like WordPress.com or Squarespace
- a self-hosted solution using an open-source project like WordPress, Drupal, Joomla
A quick search shows that WordPress is the dominant blogging platform today. Not surprising, it always has been. But I had used WordPress before and did not like it much. WordPress has many themes and plugins available. Too many. You could spend hours trying out free themes or plugins, maybe even go for paid ones, only to find out they are missing some feature or have some significant drawbacks. For example I found one really nice theme once. However it did not support styling well. I had to manually change the style of headers all over the blog because the header styling setting did not work well.
After searching some more, I stumbled on this blogging platform I had not heard of before: Ghost. It turns out Ghost was founded in 2013, and written in Node.js. I actually do not care if it is written in Node.js or another language. What attracted me was that:
- It is significantly more recent. That should result in a simpler, cleaner ecosystem of themes and plugins. Here's when each platform was first released:
- 2013: Ghost
- 2005: Joomla
- 2003: WordPress
- 2000: Drupal
- Users write articles in Markdown. This ensures the style stays consistent across pages.
Docker
My host is a VM machine from Digital Ocean. It has many services running on it:
- several Node.js apps that serve different services on their own port
- Apache serving different websites on different local ports
- Nginx to route requests to port 80 for several different domains to the right local ports.
The value of Docker is that in one command, I can set up a Ghost environment that is separate from the host. It includes Node.js, a NoSQL database, and of course Ghost. I do not have to worry whether this version of Node.js is the same as the ones that my other apps require. I don't have to manually install NoSQL and worry about which version I am installing.
First run
Fortunately there is an official image for Ghost on hub.docker.com. The documentation gives the following command:
$ docker run -d --name some-ghost ghost
This sets up an environment that runs Ghost, but it is only accessible from the machine serving it. Not very useful!
Make it remotely accessible
Remove the old one:
$ docker rm --force some-ghost
Start a new one with port forwarding:
$ docker run -d --name some-ghost -p 3010:2368 ghost
Now Ghost is available remotely on port 3010: http://blog.wafrat.com:3010/. I set up Nginx to redirect requests for blog.wafrat.com on port 80 to local port 3010 and voilà! I can see my blog on /.
Persist content to disk
Now I do not want to lose all my blog data if I ever delete the Docker instance. So I set up volume mapping. Whatever is stored in the Docker image's /var/lib/ghost/content
folder is mapped to the host VM's ~/wafrat_blog_data folder. Now I can set up automated back up on this folder.
$ docker run -d --name some-ghost -p 3010:2368 \
-v ~/wafrat_blog_data:/var/lib/ghost/content \
ghost
Fix blog url
The last issue was when I clicked on the Home link on the blog, or if I clicked on the RSS feed link, it would redirect me to http://localhost:2368. In the page settings, I can override the Home url to be /, but it does not fix the RSS feed link. A quick web search led me to this GitHub issue on the Ghost Docker image. It turns out you have to set an environment variable when running it. However it is not documented:
$ docker run -d --name some-ghost -p 3010:2368 \
-v ~/wafrat_blog_data:/var/lib/ghost/content \
-e url="http://blog.wafrat.com/" \
ghost
Nov 2018 update
After more than a year since I first set it up, it was time to update Ghost. I used the Ghost docker image documentation and this other blog post that describes how to update an image.
The basic idea is that your docker container is running an image, and you can't change that image, not even to update it to a newer version. You have to stop that container, delete it, create a new container based on the image at the desired version.
The Ghost docker container doc says that before upgrading to a new major version, you should update to the latest version of your current major version. I was at 1.8.0, and wanted to upgrade to 2.4.0. The latest at major version 1 was 1.25.0. So I first upgraded to 1.25.0, exported my data to a JSON, then to 2.4.0. I was supposed to import that JSON into the new blog. But it turned out that 2.4.0 was able to read my data folder fine.
Here are the few commands I used:
$ docker container list
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ee095d09cae6 72ed48f1b5e8 "docker-entrypoint..." 13 months ago Up 3 months 0.0.0.0:3010->2368/tcp some-ghost
$ docker container inspect ee095d09cae6
[
{
...
"Config": {
...
"GHOST_VERSION=1.8.6",
"GHOST_INSTALL=/var/lib/ghost",
"GHOST_CONTENT=/var/lib/ghost/content"
],
...
$ docker stop ee095d09cae6
ee095d09cae6
$ docker run -d --name some-ghost -p 3010:2368 -v ~/wafrat_blog_data:/var/lib/ghost/content -e url="http://blog.wafrat.com/" ghost:1.25.5
Unable to find image 'ghost:1.25.5' locally
1.25.5: Pulling from library/ghost
...
Status: Downloaded newer image for ghost:1.25.5
docker: Error response from daemon: Conflict. The container name "/some-ghost" is already in use by container "ee095d09cae6bf769743168093080b2ecc46edc7bf987241d581167814ff9771". You have to remove (or rename) that container to be able to reuse that name.
See 'docker run --help'.
$ docker rm some-ghost
some-ghost
$ docker run -d --name some-ghost -p 3010:2368 -v ~/wafrat_blog_data:/var/lib/ghost/content -e url="http://blog.wafrat.com/" ghost:1.25.5
a5946da244b0b84acf19206e4076ef1ca1ac66625046a8af13661cc85dd300d1
# At this point I went into the Ghost Admin UI and exported all blog data to a JSON file.
$ docker stop a5946da244b0b84acf19206e4076ef1ca1ac66625046a8af13661cc85dd300d1
a5946da244b0b84acf19206e4076ef1ca1ac66625046a8af13661cc85dd300d1
root@ubuntu-512mb-sfo1-01:~# docker rm some-ghost
some-ghost
$ docker pull ghost
Using default tag: latest
latest: Pulling from library/ghost
...
05a0de8ccd4d: Pull complete
Digest: sha256:a8915865ad01b66edce528dce2811c5e6591ed2bcaeecf4473b1d8266311027e
Status: Downloaded newer image for ghost:latest
$ docker run -d --name some-ghost -p 3010:2368 -v ~/wafrat_blog_data:/var/lib/ghost/content -e url="http://blog.wafrat.com/" ghost
48f41489d306caae2bf34ad08c4cd989b11637e841bde9c4078e584da0344963
Integrations
Ghost does not provide analytics or commenting out of the box. It is surprising since most others like WordPress or Medium do. Fortunately the documentation explains how to add them via "app integrations".
Analytics
This is straightforward. Set up a Google Analytics account, then add the tracking snippet to the blog header in the Code injection
tab.
Now let's check that it works. I open blog.wafrat.com/beginning-android-day-1/ with Developer Tools open. See the last line collect?v=...
? That's the Analytics beacon. It sends information to Google Analytics: what type of event is being tracked (in this case a page view), what the URL is, what the browser is, etc.
Commenting
TBD
Syntax highlighting
This blog post explains how to add syntax highlighting to Ghost. It uses Prism, a script that looks for <code> elements and colors the content according to the language that you specify. After adding the Prism CSS to our blog header, and the Prism JS to our blog footer, and adding the language to our <code> elements, we go from this:
To this:
Nov 2018 update
Going from version 0.0.1 to 1.15.0 in order to support Dart. It seems like you have to single handedly pick which languages you want to support:
So I opted for:
- Dart
- Golang
- Java
- Json
- Kotlin
Sequence diagrams
I have always liked sequence diagrams to illustrate how different parts work together. I use http://sequencediagram.org/. It is a free tool that lets you write sequences in text, then export the resulting diagram to SVG with no watermark.