Setting up a blog with Ghost is extremely easy. You don't need to worry about NodeJS, SQL databases, or whether your machine will be able to run it. Last time I used Ghost, I remember messing around with NodeJS and NPM. Nowadays, it's much easier. You can run this on a machine with 1 CPU core and 512MB of RAM (you can get one of these on the GCP free tier).

At the end of this guide you'll have a Ghost blog with a free, auto-renewing SSL certificate.

Installing Ghost and Nginx

First, install Docker. Your Linux distribution's version of Docker is just fine.

sudo apt install -y docker.io
sudo systemctl enable docker
sudo gpasswd -a YOURUSERNAME docker
# now, log out of the terminal and log back in

Next, run:

mkdir -p $HOME/content
docker run \
    --name ghost \
    -p 2368:2368 \
    -e url=https://example.com \
    -v $HOME/content:/var/lib/ghost/content \
    --restart=always \
    -d \
    ghost:alpine

You should save this in a script called create.sh if you ever need to recreate the container.

Next, install nginx (sudo apt install -y nginx) and modify the configuration:

# /etc/nginx/nginx.conf:

user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;

events {
	worker_connections 768;
}

http {
	sendfile on;
	tcp_nopush on;
	tcp_nodelay on;
	keepalive_timeout 65;
	types_hash_max_size 2048;
	server_tokens off;

	include /etc/nginx/mime.types;
	default_type application/octet-stream;

	ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
	ssl_prefer_server_ciphers on;

	access_log /var/log/nginx/access.log;
	error_log /var/log/nginx/error.log;

	gzip on;

	include /etc/nginx/conf.d/*.conf;
	include /etc/nginx/sites-enabled/*;
}

# /etc/nginx/sites-enabled/default:

server {
    server_name example.com;
    listen [::]:80;
    listen 80;

    location / {
        proxy_pass http://127.0.0.1:2368;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $http_host;
        proxy_set_header X-Forwarded-Proto https;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

Now, start Nginx: sudo systemctl restart nginx

Now we'll set up an SSL certificate using Let's Encrypt and CertBot. You'll need to get your Linux distribution's exact instructions from this page: https://certbot.eff.org/lets-encrypt/debianbuster-nginx

Select "Nginx" and your distribution. On Debian, it was as simple as:

sudo apt-get install certbot python-certbot-nginx
sudo certbot --nginx
sudo reboot

When the system starts back up, your Ghost blog should be up and running with SSL!

Updating Ghost

When it's time to update, all I have to do is:

docker stop 123
docker rm 123
docker pull ghost:alpine
./create.sh

Since my content is mounted to the container, I can delete and recreate the container without worrying.

Backups

Backing up the content is straightforward too. Since I'm using Ghost with SQLite, all I need to do is back up my content directory. I use rclone to back this up to Backblaze B2 (10GB free!) on a daily basis.

FILENAME=backup_`date '+%Y%m%d'`.tar.gz
tar -zcf backups/$FILENAME content
rclone copy backups/$FILENAME b2:/my-bucket/blogbackup/

That's all there is to it. I'm very happy with my lightweight Ghost blog set up!