After deploying custom themes with custom docker containers for a few years, I recommend against it unless you are already maintaining and monitoring a robust automation and monitoring pipeline.
See this article for a simpler approach to custom themes.
This guide covers a simple approach to deploy ghost with docker and a custom theme. We will use the official ghost themes as a base, but any custom theme will do just fine.
Preparing a dev environment
- Get a Docker Hub account
- Install Docker on your workstation
- Authenticate your Docker Hub account to your workstation with
- Install the current LTS version of node.js, preferably using nvm
- Install the ghost-cli@latest npm package
- Create a directory for this project's work files. This guide presumes a project directory located at
Create a local ghost install
From a terminal in your project root, install a local ghost server
# Switch to your project directory
# Create a folder for the ghost installation
# Install a local ghost server
ghost install local
Create an admin user
Visit your local ghost installation and create your admin user and password by visiting http://localhost:2369/ghost.
Clone the Ghost Themes repository
Clone the Ghost Themes github repository to your project directory.
# Switch to your project directory
# Clone the themes repo
git clone [email protected]:TryGhost/Themes.git
Your project folder should now look like this.
Link local Ghost with your customized theme
For this guide, we'll customize the Journal theme.
Working dev git branch
If this section is gibberish to you, it can be safely ignored.
Create a branch for your customizations. You can rebase your branch with main in the future. By rebasing your branch, you can get all the updates to the official theme over time as Ghost is updated, while preserving your customizations in your own branch. Pull the
main branch from the offiaial repo, push your
custom branch to your own git repository.
Link the custom theme into local Ghost
Update the theme's code and see the changes relected live within your local ghost server at http://localhost:2369. This is done by creating symbolic link from your themes repository into the content directory of your local ghost server.
ln -s \
Once the link is created, activate the custom theme from the ghost admin panel, under Settings > Design > Brand > Themes
Compile the theme during development
While working with your theme files, run the themes package
dev target to compile all your assets as you make changes.
# Change to the themes repository
# Automatically compile assets when changed
Customize Your Ghost Theme
Actually working with the theme files is outside the scope of this guide. The themes are very friendly and accessible to customize. If you need help, look to the Ghost Theme Documentation to get started.
Deploy with Docker
Build the Dockerfile
When you're ready to deploy your theme, create a dockerfile based on the official ghost container. This dockerfile will copy your custom theme to /opt, and symbolically link it into the ghost content directory. By using a link, instead of copying the theme directly into the content directory, deployments of your container can safely mount docker volumes for the content directory and still get updated theme files with container updates.
Build and publish your container
# Use your own dockerhub user and repo below, of course
# Build the container, add latest and timestamp labels
docker build \
-t mjac/ghost-custom:latest \
-t mjac/ghost-custom:$(gdate +%Y-%m-%d_%H%M) \
# Push to container registry
docker push mjac/ghost-custom
Deploy your container with a compose file
Next steps and thoughts
I would like to improve this workflow.
With this approach, when ghost pushes a new official image, I have to pull the new image, rebuild and republish this custom container. I want to, without needing a jenkins infrastructure, or proprietary github, get the custom theme container rebuilt automatically and published when ghost is updated.
Ghost offers tools for this involving github actions, through jenkins, connecting to the ghost api on your ghost website and uploading a new theme distribution bundle. Their approach is github proprietary, and doesn't please me. I want updates to the theme to be delivered as part of the container, instead of living as data injected into a data volume after the fact.