Health check for Ghost Docker deployment

An illustration of a happy blue whale (a tribute to old docker logo)
Generated with Dall-e

I'm playing with Ghost for a while to host many websites as easy as possible. I'm a fan of Docker Swarm to manage a production environment that I can host many projects in an easy way. (For more details how to do that, you can check great articles from my friend Uğur Aslan)

Ghost team officially doesn't provide a Docker image. But there is a popular community-driven Docker project. When you want to deploy a Ghost website via Docker, you will notice a short downtime (around a few seconds) while updating the container. With Docker Swarm deploy configuration, you can assure that first running a new instance and then stop the previous version.

services:
  mysite:
    image: ghost:latest
    environment:
      url: https://mysite.com
    volumes:
      - settings:/var/lib/ghost/content/settings
      - themes:/var/lib/ghost/content/themes
      - data:/var/lib/ghost/content/data
    deploy:
      replicas: 1
      update_config:
        parallelism: 1
        order: start-first
        failure_action: rollback
        delay: 20s
      rollback_config:
        parallelism: 0
        order: stop-first
      restart_policy:
        condition: on-failure
        delay: 5s
        max_attempts: 3
        window: 120s

With this config, we order that a new container will be run in parallel, will wait for 20s and if there is no failure then previous container will be stopped. If there is a failure, a rollback will be executed to the previous version. But the problem is, sometime initialisation of Ghost instance can take more time. Ghost does DB migrations, cache filling etc. So we need to really be sure that our new instance is healthy. Fortunately, Docker gives us a chance to check health of running instance by running a command inside the container. Basic and most popular way of doing that check is doing a curl request to a URL. If that URL responds well, than we assume that container is healthy.

Generally, services provides a way to check system health. Like a command or a lightweight HTTP endpoint. Ghost doesn't provide an endpoint specifically but it's suggested to use an Admin API endpoint which doesn't require authentication and returns basic information about the running website (which is a good functionality for a healthcheck).

services:
  mysite:
    ...
    healthcheck:
      test: "curl -H 'X-Forwarded-Proto: https' -If http://localhost:2368/ghost/api/admin/site || exit 1"
      interval: 30s
      timeout: 10s
      retries: 5

With this simple addition, we make a http request to running Ghost internally, and accept a 200 response. -f parameter of curl assures to have a failure if we don't have a successful response. And || exit 1 makes ending this whole command successful if there is no error happens with curl. Other properties are very obvious: "Try running this command up to 5 times with 30 seconds intervals and wait command to finish up to 10 seconds".

You may noticed X-Forwarded-Proto: https part. By default, if Ghost is configured to run using SSL, all of the http requests are redirected to https equivalent. Since this is a "failure" for our curl command and we can not call https://localhost easily, the workaround is adding X-Forwarded-Proto: https header which directs that TLS is handled by a proxy, so Ghost consider this request as a secure one. You may also question why we just don't call the production URL, like curl -f https://mysite.com/ghost/api/admin/site . This may also work for most of the times but there can be many issues causes to fail this request which is not related to our container's health. SSL may be expired or not renewed yet, DNS can be faulty, main ghost url could be changed etc. Especially, if you auto-generate SSL certificates while your Docker instance running, it's most likely this healthcheck will fail and your SSL could never be generated.

To be brief, if you want to deploy a Ghost websites via Docker, don't miss adding a healthcheck, and here you have a running configuration.

Me on Mastodon: https://synaps.space/@murat