/ Tech

Ghost with Caddy and LetsEncrypt (CertBot)

After years and years of running this blog off WordPress and the truly heinous performance overheads, not to mention the extra load generated by plugins trying to work around each other, I decided to switch over to something far more lightweight.

Enter Ghost.

Ghost is a lightweight, NodeJS-based blogging engine that has been in development for some time and is aimed at writers who just want to write rather than manage a fully-fledged CMS. It does one thing and does it well.

Oh and it has a running memory footprint of 150MB or so.

Yay!

Under higher loads it can go a bit haywire (as I found out after running ab -c 10 -n 5000 against it while it's on a single-core CPU). If you're wanting to run a high-traffic blog, you will need to implement a full-page caching layer in front of it, but for simplicity's sake, I've opted for a bare-bones setup.

The Setup

(the internets) -> caddy:443 -> ghost

You will need:

  • Ghost
  • Caddy
  • Supervisord

Ghost

Install Ghost somewhere on your server - ideally also create a user for it. You can download the source from here: https://ghost.org/developers/

Create a user that will run your ghost instance - do not run it as root as that is all sorts of bad - sudo useradd -m ghost will do you in most instances.

Change to that user with su ghost and download the Ghost files to the user's home directory - wget https://github.com/TryGhost/Ghost/releases/download/0.11.4/Ghost-0.11.4.zip

Unzip the files with unzip -d ~/blog Ghost-0.11.4.zip. This will drop all the necessary files inside /home/ghost/blog

You'll need to fiddle with the config.js file in there to set up Ghost to your liking.

The production section is where your blog config sits. Alter the url to your blog's URL and remember to include the https:// in the URL. If you don't, Ghost won't know it's running on SSL and will complain.

Pay attention to the server block of the config file. You'll need the information in the next step. It should look like this:

server: {
            host: '127.0.0.1',
            port: '2368'
        }

Caddy + Letsencrypt

This is where the fun stuff happens. I've written about LetsEncrypt in the past - it's awesome and everyone should use it if possible. The biggest drawback is the setup. It's a bit of a pain in the ass to get running and remembering certificate renewals is also a chore. While you can script that stuff, it's still a bit of a ball-ache.

Caddy is a lightweight HTTP server written in Go (Go is awesome, go check it out) - you can download it pre-built here: https://caddyserver.com/. One of the major benefits of Caddy is that it's a single binary file. There are no libraries to install, no prerequisites and weird Linux compatibility issues, etc. It just works.

Move the file to somewhere like /usr/local/bin/ so it's on the system path and you're good to go.

The Caddyfile has its own json-esque syntax so it's pretty straightforward - full docs here - but for our purposes, we need something like this.

The Caddyfile can go in /etc/caddy/Caddyfile which is what we'll be using in this example. Copy the text below into that file.

https://www.yoursite.com {
    proxy / http://127.0.0.1:2368 {
	    header_upstream Host {host}
        header_upstream X-Real-IP {remote}
	    header_upstream X-Forwarded-For {remote}
        header_upstream X-Forwarded-Proto {scheme}        
    }
    tls youremail@example.com
}

You will need the bits from the server block of your config.js file in Ghost. The header directives tell Caddy to pass on certain HTTP headers to Ghost to let it know that it's sitting behind an SSL web server that is handling SSL termination.

If Ghost does not see those headers, you will end up in a redirect loop as Ghost keeps trying to redirect you to https://yoursite.com/

The tls part of that config is to set your email address so the LetsEncrypt CA knows where to send important information.

Supervisor

Supervisor is a process manager (you can also use systemd or upstart or the init system). It generates very little overhead and doesn't require tinkering with the system's own services to make your stuff work and is perfect for ad-hoc process management.

You will need two config files for it, placed inside /etc/supervisor/conf.d/:

caddy.conf

[program:caddy]
command=/usr/local/bin/caddy -conf="/etc/caddy/Caddyfile" -agree=true -root=/var/tmp
autostart=true
autorestart=true
startsecs=10
stdout_logfile=/var/log/caddy.log
stdout_logfile_maxbytes=1MB
stdout_logfile_backups=10
stdout_capture_maxbytes=1MB
stderr_logfile=/var/log/caddy_error.log
stderr_logfile_maxbytes=1MB
stderr_logfile_backups=10
stderr_capture_maxbytes=1MB

ghost.conf

[program:ghost]
command = node /home/ghost/blog/index.js
directory = /home/ghost/blog
user = ghost
autostart = true
autorestart = true
stdout_logfile = /var/log/supervisor/ghost.log
stderr_logfile = /var/log/supervisor/ghost_err.log
environment = NODE_ENV="production"

Once that's done, you can run supervisorctl update and Supervisor should pick up the new files and start the respective processes.

Caddy will start and you will see something like

Activating privacy features... done.
https://www.yoursite.com

in /var/log/caddy.log

Congratulations! You now have a SSL-secured, Ghost-powered blog!