Deploying Ghost Docker

Ghost, the CMS platform this runs on, is one of the many options available for self-hosted blogs. The objectives were to have a platform that could be rapidly re-deployed, and maintained persistence across deployments via an external database.

The OS platform used was Amazon Linux 2023 for both Docker host and MySQL database. The MySQL Database lives on its own VM to simplify backups, management, and performance.


Deploying MySQL

Installing MySQL on the Amazon Linux 2023 VM. These steps were inherited for a dev.to article.

sudo wget https://dev.mysql.com/get/mysql80-community-release-el9-1.noarch.rpm 
sudo dnf install mysql80-community-release-el9-1.noarch.rpm -y
sudo rpm --import https://repo.mysql.com/RPM-GPG-KEY-mysql-2023
sudo yum install mysql mysql-server mysql-libs mysql-server

Fast Initialize MySQL.

sudo mysqld --defaults-file=/etc/my.cnf --initialize --user=mysql

This will create the root user with a temporary password. The password may not be displayed after the command runs, so check /var/log/mysqld.log for the password generated.

Connect to the MySQL instance using the temporary password:

mysql -u root -p

Once in the MySQL server, create a new database and user. If you know the IP Address your Docker host is on, replace % with the IP Address.

CREATE DATABASE {ghost_db_name};
CREATE USER '{db_usr}'@'%' IDENTIFIED BY '{Password}';
GRANT CREATE, ALTER, DROP, INSERT, UPDATE, DELETE, SELECT, REFERENCES, RELOAD on {ghost_db_name}.* TO '{db_usr}'@'%';

Deploying Ghost on Docker

Ghost's configuration documentation leaves a lot of be desired, but the best tidbit is that environment variables mirror all configuration paths, with nesting delineated by __. See example below, where we (try) to leverage Direct Send mail through Outlook.

docker run -d \
    --name some-ghost \
    --pull=always \
    -e NODE_ENV=production \
    -e url='http://blog.noloc.net' \
    -e database__connection__host='{MySQL_DB_IP}' \
    -e database__connection__port=3306 \
    -e database__connection__user='{MySQL_USER}' \
    -e database__connection__password='{MySQL_PASSWORD}' \
    -e database__connection__database='{MySQL_DB}' \
    -e mail__from='[email protected]' \
    -e mail__transport='smtp' \
    -e mail__options__host='noloc-new.mail.protection.outlook.com' \
    -e mail__options__service='Outlook' \
    -e mail__options__port='25' \
    -e mail__options__secure=True \
    -e mail__options__auth__user='[email protected]' \
    -e mail__options__auth__pass='NoPass' \
    -p 80:2368 \
    ghost:alpine

Things I've observed:

  • Documentation specifies url is mandatory; It is not. It does impact where automatically generated URLs:
    • The default About page.
    • The in-page post creation preview window.
  • The Docker container listens on TCP 2368 internally, not TCP 80 like standalone, and does not use HTTPS.
  • If you intend at load-balancing between multiple containers, ensure they are using the image at all times. Easiest way is to either version lock (e.g. ghost:5.100.1-alpine), or to have --pull=always and re-deploy during the same maintenance window.