Why & How Ghost
This blog post documents my journey in setting up my blog. From the decision of which platform to use, to the rollout using docker-compose.
To make life for myself easier, and maybe to help people, I will be documenting all my projects on this site. This first entry will be about how I got this blog running.
Decisions
The first step was the most difficult: deciding what platform to use. After researching and testing multiple tools, I chose Ghost. It was a close call between Ghost and WriteFreely. And while Ghost has more than I was looking for, WriteFreely missed two important (for me) features.
- It doesn't have a production-ready container image. And while I could have made one myself, there was still issue number 2.
- No search/categories/tags. While this isn't an issue for a simple blog, it makes it hard to find the right posts.
Architecture
Ghost doesn't need much. A server to run on, a database to keep data and a reverse proxy to handle requests and ssl.
I decided to run it using Docker. I'm a fan of containers, they make life easy. And although there are alternatives available (Podman, Kubernetes, ...). Docker (and docker-compose) is all I need and what I know best.
The data is kept in a sqlite database. I could have gone for a MySQL instance. But I like to keep things simple. And I don't expect performance to be an issue.
Lastly there is Nginx Proxy Manager to be used as a revers proxy and handle ssl certificates. How I got this set up will be its own post soon (I hope).
Implementation
To get things up and running I created an empty directory. In here are two files, docker-compose.yml
and .env
.
Compose
The first part of the docker-compose.yml
file contains the following:
version: "3.1"
networks:
default:
external:
name: npm_network
As you can see, the docker-compose.yml
file contains 3 parts: version, networks and services.
I use version: "3.1"
because that's what the official documentation tells me.
The network part specifies to use the external docker network npm_network
as default for this compose file. The npm_network
is a network to which all my services are connected. This allows me to use Nginx Reverse Proxy to connect directly to the container. Because of this I don't have to expose a port to my host. I will show you how to create this network in my next post.
If you don't want to wait until the second part. Or if you want to use a different (or no) reverse proxy, you'll have to expose the port in the services part. You will also need to delete the
network
part from the compose file.
The next part contains the service itself:
services:
ghost-app:
image: ghost:4-alpine
volumes:
- ${DATADIR}/app:/var/lib/ghost/content
restart: unless-stopped
environment:
url: ${APP_URL}
database__client: sqlite3
useMinFiles: "true"
compress: "true"
imageOptimization: "true"
This defines a single service called ghost-app
. It uses the official ghost:4-alpine
image. This is version 4 of Ghost, on an alpine-based base image. Using alpine makes the image smaller. It hasn't has as many tools as some other base images, but as we won't be manipulating the image that's no issue.
restart: unless-stopped
means the container will be restarted if something goes wrong.
We specify one volume. This volume will contain all the data Ghost needs. So we data will stay persistent when something goes wrong with the container. This also means we can backup the data from the host. And backups are important!
I chose a few environment variables to configure ghost. More info about all available parameters can be found here. url
tells ghost what the base URL of the instance will be. By default the image should use sqlite, but just to be sure I defined it in the variables. useMinFiles
, compress
and imageOptimization
are three parameters I'm using in my conquest to keep traffic between my server and your browser to a minimum.
Variables
As you can see in the compose file, we make use of some variables. This is because I host all my compose files on GitHub and I don't want people to know my little secrets.
To fill in these variables we put a .env
file in the folder. This file contains the variables:
DATADIR=/media/docker/ghost
APP_URL=https://ghost.krokantekrab.be
for this once you can see what I've put in the file. The APP_URL
is this website, and DATADIR
is a folder on the host in which the data is stored.
Launch
Using docker-compose config
you can see how the result will look with all variables filled in:
root@jveweb:/opt/DockerStacks/ghost# docker-compose config
networks:
default:
external:
name: npm_network
services:
ghost-app:
environment:
compress: "true"
database__client: sqlite3
imageOptimization: "true"
url: https://ghost.krokantekrab.be
useMinFiles: "true"
image: ghost:4-alpine
restart: unless-stopped
volumes:
- /media/docker/ghost/app:/var/lib/ghost/content:rw
version: '3.1'
Now just run docker-compose up -d
and the service will be started in the background. Configure your reverse proxy (or wait for my next article) and you're done!
And that's it! Thats how what you're see is running. I hope you found it useful. If you got questions, don't hesitate to contact me!