Setting up Ghost
Assuming you're using a private Ubuntu Server, Ghost will run in a container for convenience, with a local MySQL instance, and Apache2 as a reverse proxy.
Step 1 - install docker
Instructions at https://docs.docker.com/engine/install/ubuntu/, installing from docker's apt repo:
sudo apt-get update
sudo apt-get install \
ca-certificates \
curl \
gnupg \
lsb-release
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin
Test docker with:
sudo docker run hello-world
Step 2 - Setup MySQL User and DB
Create a user "ghost", a database with the same name, and give the ghost user all rights to the ghost database:
CREATE USER 'ghost'@'localhost' IDENTIFIED BY 'passwordxxx';
create database ghost;
GRANT ALL PRIVILEGES ON ghost.* TO 'ghost'@'%';
Step 3 - Configure MySQL to listen to Docker gateway IP
For containers to see services on the host, like MySQL / MariaDB, those services need to listen to the Docker network's gateway IP - 172.17.0.1 by default.
For MySQL, edit the MySQL config file:
sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf
and change "bind-address" to append the gateway IP address:
bind-address = 127.0.0.1,172.17.0.1
Some older versions of MySQL don't support multiple different listening addresses, in which case YMMV.
Step 4 - setup the Ghost docker container
Create a folder to store Ghost content outside container, and the docker compose YAML config file:
mkdir ~/ghost
and then create ghost.yaml in folder, replacing XXX as required (assuming you're using a local MySQL database, with a database user "ghost" and database named "ghost", setup in the next step):
version: '3.1'
services:
ghost:
image: ghost:latest
restart: always
ports:
- 2368:2368
volumes:
- /XXX/ghost/content:/var/lib/ghost/content:z
environment:
# see https://ghost.org/docs/config/#configuration-options
database__client: mysql
database__connection__host: 172.17.0.1
database__connection__user: ghost
database__connection__password: passwordxxx
database__connection__database: ghost
url: https://blog.johnswitzerland.com
# defaults to PROD env unless you override here
#NODE_ENV: development
Use docker compose to fetch, configure and start the latest Ghost container image:
docker compose -f ghost.yaml up
Step 5 - Apache as a Reverse Proxy
I chose Apache2, NGINX is also an easy substitute.
Apache needs the following modules enabled:
- headers
- proxy_http
And config for the site needs to include something like this:
RequestHeader set X-Forwarded-Proto "https"
SSLProxyEngine on
ProxyRequests Off
<Proxy *>
Order deny,allow
Allow from all
</Proxy>
ProxyPreserveHost On
ProxyPass "/" "http://localhost:2368/"
ProxyPassReverse "/" "http://localhost:2368/"
<Location />
Order allow,deny
Allow from all
</Location>
Where:
- RequestHeader is required to avoid Ghost redirect loops (endless 301/302 responses) - letting Ghost know Apache is handling SSL and not to try and redirect to a secure link
- ProxyPreserveHost is required to pass your full Ghost public domain name to Ghost, rather than the internal address specified by ProxyPass