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.
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.
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).
To get things up and running I created an empty directory. In here are two files,
The first part of the
docker-compose.yml file contains the following:
As you can see, the
docker-compose.yml file contains 3 parts: version, networks and services.
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
networkpart from the compose file.
The next part contains the service itself:
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.
imageOptimization are three parameters I'm using in my conquest to keep traffic between my server and your browser to a minimum.
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:
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.
docker-compose config you can see how the result will look with all variables filled in:
root@jveweb:/opt/DockerStacks/ghost# docker-compose config
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!