Ghost With Gitlab CI/CD
Discover how to integrate GitLab with Ghost CMS for auto-deploying themes, a feature commonly associated with Github. This post serves as a comprehensive guide for those using a self-hosted GitLab instance and struggling to find resources on this integration.

As you might have noticed, this website uses Ghost CMS. Ghost officially supports Github as integration which allows users to auto-deploy themes. However for my use case, I use a self-hosted Gitlab instance for my code repository and after searching on how to integrate Gitlab with Ghost, I did not find anything. Hence this post will show how to integrate Gitlab with Ghost.
Setup
First we need to create an integration in order to create an admin api key. Integrations -> Custom
.

After copying the Admin Api key
we navigate to the our Gitlab instance.
Settings -> CI/CD -> Variables

The GHOST_ADMIN_API_KEY
contains the Admin Api key
we copied from the Ghost instance. The GHOST_URL
contains the url to the ghost instance e.g. https://example.com
Gitlab CI/CD
After creating our project in Gitlab, create a new file .gitlab-ci.yaml
with the following contents
image: node:latest
stages:
- test
- deploy
cache:
key: $CI_PROJECT_NAME
paths:
- node_modules/
test_theme_with_gscan:
stage: test
script:
- npm install
- npm install -g gscan
- npx gscan .
deploy_to_ghost:
stage: deploy
rules:
- if: $CI_COMMIT_TAG
script:
- apt update
- apt install -y zip jq
- npm install @tryghost/admin-api
- npm version --no-git-tag-version ${CI_COMMIT_TAG} #Change the version in package.json to the tag version
- zip -r $(jq -r '.name' package.json).zip . -x *.git* *.zip yarn* npm* node_modules/\* *routes.yaml *redirects.yaml *redirects.json deploy.js .gitlab-ci.yml .idea/\*
- node deploy.js
This file has two stages, test
and deploy
, in the test stage it will install the dependencies and run gscan
. This will verify that the theme is compatible with the latest version of ghost.
Once the test stage successfully runs, then comes the deploy stage. This stage will only run if there is a tag. The version of the theme is update with the tag version and the deploy.js
script is ran.
In the project folder add a new file deploy.js
with the following content
const path = require('path');
const process = require('process');
const {exec} = require('child_process');
// The admin API client is the easiest way to use the API
const GhostAdminAPI = require('@tryghost/admin-api');
(async function main() {
try {
// Configure the client
const api = new GhostAdminAPI({
url: process.env.GHOST_URL,
key: process.env.GHOST_ADMIN_API_KEY,
version: 'v5.0'
});
const basePath = path.join("/builds",process.env.CI_PROJECT_PATH);
const pkgPath = path.join(basePath, "package.json");
const themeName = require(pkgPath).name;
const themeZip= `${themeName}.zip`;
let zipPath = path.join(basePath, themeZip);
// Deploy it to the configured site
await api.themes.upload({file: zipPath});
console.log(`${zipPath} successfully uploaded.`); // eslint-disable-line no-console
} catch (err) {
console.error(err); // eslint-disable-line no-console
process.exit(1);
}
}());
The main purpose of this file is to deploy the theme to the ghost site.
Now whenever a push is performed the test stage will run and when a tag is pushed both the test and deploy stage will run.
Below is a POC to generate a JWT from the admin api key.