UPDATE: This blog no longer runs on Ghost, as I migrated to Hugo in 2025. This post is is still here in case you want to acheive the same thing, but you can read about the migration and the reasons for it here . This guide also won’t work with managed Ghost providers like midnight.
DesigningOverload is (was) a Ghost
blog.
Ghost
is great as a framework for blogging,
and one of the really excellent things about it is the very flexible
theming apparatus for making your blog look great. What isn’t
immediately obvious is how to really seamlessly push changes to that
theme directly to the blog. This post is about my journey to get that working.
The ubiquitous theme for ghost is Casper , which is clean, simple, and well documented. A great starting point for building your own theme. The documentation for how to edit it as a theme is excellent on their own README page, so rather than going into that here I’ll focus on how to deploy it directly.
For continuous deployment of other projects I’m previously used both CircleCI and Codeship . As I’ve been using the former most recently for sqlfluff , that is the one that I’ll be using for this project.
The last piece of this puzzle is the infrastructure for running the
blog itself. For simplicity this blog is (was) running on
DigitalOcean
, using their
ghost 1-click app
.
If you’ve never used a web hosting platform before, DigitalOcean is
a nice balance between simplicity and functionality. For people who’ve
used full-bore AWS
or GCP
before, you won’t find all the same features in DigitalOcean, but
for simple projects there’s plenty there for you.
1. Find a theme & fork it#
If you haven’t already, find a theme that you like on Github (I chose Casper, but this should work with others too), and fork it. Name the new respository something different and update the README file so that people don’t get confused. I named my fork OverloadCasper and you can find it here . Make sure you set up your new repository as a public repository, otherwise some of the services we’ll rely on being free later won’t be.
2. Configure it with a new name#
Within the newly forked repository, find the package.json
file and update
it so that your new theme will look like a new package to anything that
tries to load it. The summary of the fields that I’ve updated are:
{
"name": "overload-casper",
"description": "The theme for designingoverload.com, forked from Casper",
"demo": "https://designingoverload.com",
"version": "0.2.0",
...
"author": {
"name": "Alan Cruickshank",
"email": "seegithub@ifyoureally.care",
"url": "https://designingoverload.com"
},
...
"repository": {
"type": "git",
"url": "https://github.com/alanmcruickshank/OverloadCasper.git"
},
...
}
3. Set up CI#
Now we have a theme, we need to get something to build it automatically. This is where CircleCI comes into the picture. They offer free accounts for open-source projects, so head over to their site and set up a free account. Once you’re set up and linked to your GitHub account it will ask which repositories you want to follow, select the repository which you created in step.
4. Configure a deploy job#
Pretty soon CircleCI will ask you to configure the jobs and workflows
for your project. You do this with a yaml
file placed at .circleci/config.yml
in your repository. How to set this up for a ghost theme is very not obvious.
A very simple starter config.yml
file which should work for your theme would be:
version: 2
# Define the jobs available
jobs:
build:
docker:
- image: circleci/node:10.18
working_directory: ~/repo
steps:
# Fetch the code from Git
- checkout
# Try the standard install for a ghost theme
- run: npm install
# Test that the theme builds
- run: yarn test
# Define the workflows to run
workflows:
version: 2
build_and_deploy:
jobs:
- build
5. Test your deploy job#
Try making a small change to your repository, and pushing the changes to your remote on Github. You should see a build start in the interface for CircleCI, in the same form that you can see the equivalent builds for OverloadCasper here .
6. Generate SSH keys#
Congratulations, you’re now automatically testing changes to your theme. The theme still isn’t on your blog itself though. To do that we need to integrate our DigitalOcean instance with CircleCI, and we’ll be doing that using ssh. Safety warning: Be very careful where you put the ssh keys to your instances. They will grant access to your servers for any attackers who can then alter/delete/copy your data in any way they like. YOU HAVE BEEN WARNED!. You should generate a fresh ssh key just for this purpose, github has quite a good guide on how to create a fresh ssh key on your platform .
7. Alow your new SSH key on the remote server#
SSH onto the box you’ve been using to host your ghost blog. First, using
the root
user, open up the /etc/ssh/sshd_config
file. At the very end
you’ll want to add a line which says AllowUsers ghost-mgr
, this enables
ssh access for the ghost-mgr
user which is used to… manage ghost. Next
you’ll want to switch to that user (using sudo -i -u ghost-mgr
) to say what
keys are allowed to log in as it. Create a directory in the home
directory
for that user called .ssh
and in there create a file called authorized_keys
.
In there we want to add a new line at the end, adding the public part of
your new key. It will look something like:
ssh-rsa AAAAAAAALonGstr1ng0fnumb3r5AnDL3tters= Potentially with a Comment
You should now be able to ssh onto that box using an ssh client of your choice.
8. Configure SSH access for your CI provider#
Head into the CircleCI interface, go to the settings for your project and
there should be an option labelled SSH Permissions
, and within that a button
to add a new ssh key. Leave the hostname blank, and paste in the private key
(in openssh format, which you’ll need to specifically select if you’re using
putty
to generate keys on windows). This will then be the default key to
use if CircleCI needs to try and ssh onto a box as part of any of the workflows
associated with this project.
9. Package the theme on build#
Almost everything is now in place. The last things to do are to make sure
the theme is packaged properly, send it to your live server and then switch
to the new theme. To do the first of those, alter the .circleci/config.yml
file slightly to add a line to make a zip
package of the theme, and then
we’ll persist that file to the
circleci workspace
.
It will now look something like…
version: 2
# Define the jobs available
jobs:
build:
docker:
- image: circleci/node:10.18
working_directory: ~/repo
steps:
# Fetch the code from Git
- checkout
# Try the standard install for a ghost theme
- run: npm install
# Test that the theme builds
- run: yarn test
# Build the package
- run: yarn zip
# Store in a way we can reach later
- persist_to_workspace:
root: dist
paths:
- "*.zip"
# Define the workflows to run
# <same as before>
10. Persist packaged theme to remove server#
We’ll now set up a second job to take this file that we’re persisted and
copy it onto our remote server. The full example is in the config.yml
file for OverloadCasper here
. The important things to note are:
- in the
workflows
section near the end, there’s a reference to a new job calleddeploy_master
which depends on thebuild
job. - further up the file, that
deploy_master
job is defined with a few steps that first attach the previosly persisted workspace, then recognise the host fingerprint of your remote server in theknown_hosts
file. Finally, we cope the file across, unzip and copy it into the revelant directory, restart ghost and then do some cleanup. - the bash commands refer to
OVERLOAD_HOST
,OVERLOAD_FINGERPRINT
andOVERLOAD_USER
, all of which are environment variables configured within the circleci interface. Putting them here, means you don’t have to put them in your public github repo, and means that your data remains more secure.OVERLOAD_HOST
should be the public IP address of your remote server on DigitalOcean.OVERLOAD_USER
should beghost-mgr
as this is the user we configured earlier.OVERLOAD_FINGERPRINT
should be of the formecdsa-sha2-nistp256 AAAXYZ...=
and you can find by runningssh-keyscan localhost
while ssh’d onto your remote server.
Done#
And that’s it. The first time you do this you’ll need to go into the interface for your ghost blog and manually switch to the new theme, but from that point onward, this automation will replace that original theme and so any updates will flow through automatically.
This post was also written retrospectively, so there may a few steps which might need a little extra customisation for your use case. If you find them and think that it’s worth updating this little tutorial, feel free to raise them as issues on the GitHub page for OverloadCasper .
Good luck!