Welcome to this guide on how to run Ghost CMS in Docker Containers. Ghost CMS is an open-source content management system. It is used to create professional and awesome-looking online blogs. It is built on modern Node.js technology to provide the required power, flexibility, and performance.
Ghost was created by John O’Nolan and Hannah Wolfe in 2013. John O’Nolan, one of the core contributors of WordPress watched as the platform became complicated over time and decided to kickstart the Ghost project. With time, it grew in popularity as the demand for open-source CMS became evident. Currently, it is used in production by companies such as Apple, DuckDuckGo, Sky News, OpenAI, Square, Bitcoin Foundation, Tinder, Mozilla e.t.c
Ghost is preferred over other tools due to the following:
- Independent structure – It is structured as a non-profit organisation to ensure it can legally never be sold and will always remain independent, building products based on the needs of its users
- Distributed team – There are open source contributors to work on Ghost full-time, and we do this entirely remotely. The core Ghost team is fully distributed and live wherever they choose.
- Unconditional open source – Projects are released under the permissive open source MIT licence, so that even if the company were to fail, our code could still be picked up and carried on by anyone in the world without restriction.
The amazing features associated with Ghost CMS are:
- Custom themes – It ships with a simple Handlebars.js front-end theme layer which is very straightforward to work with and surprisingly powerful. The Ghost Theme Marketplace provides a selection of pre-made third-party themes which can be installed with ease
- Apps & integrations – It is built as a JSON API, has webhooks, and gives you full control over the front-end: It essentially integrates with absolutely everything.
- Search engine optimisation – It comes with a world-class SEO and everything you need to ensure that your content shows up in search indexes quickly and consistently.
- Rich editor – It has has the richest editor that every writer wants, but under the hood it delivers far more power than you would expect. All content is stored in a standardised JSON-based document storage format called MobileDoc, which includes support for extensible rich media objects called Cards.
- Roles & permissions – It allows one set up the site with sensible user roles and permissions built-in from the start. These include; Contributors, Editors, Administrators, Authors, and Owner
This guide will help you deploy Ghost CMS in Docker Containers. Docker makes it easy to set up Ghost CMS since all the Ghost dependencies are encapsulated and the deployment kept self-contained.
Before you Begin.
You will need the following for this guide.
- A fully Qualified Domain name.(For SSL certificates)
- Docker and Docker-compose
You also need to update your system and install the required packages:
## On Debian/Ubuntu sudo apt update && sudo apt upgrade sudo apt install curl vim git ## On RHEL/CentOS/RockyLinux 8 sudo yum -y update sudo yum -y install curl vim git ## On Fedora sudo dnf update sudo dnf -y install curl vim git
1. Install Docker and Docker Compose on Linux
For this guide, you need the Docker engine installed. This can be achieved using the dedicated guide below:
Once installed, add your system user to the docker group.
sudo usermod -aG docker $USER newgrp docker
With docker successfully installed, proceed and install Docker-Compose using the aid from the guide below:
Ensure that the Docker engine is up and running:
sudo systemctl start docker && sudo systemctl enable docker
2. Provision the Ghost CMS Container.
The Ghost CMS deployment file contains 3 parts:
- Ghost CMS
- Database(MySQL) for data storage
- Web server(Nginx) for reverse proxy
Now create the docker-compose file.
mkdir ghost && cd ghost vim docker-compose.yml
In the file, add the below content and replace the domain name and the database root password appropriately.
version: '3' services: ghost: image: ghost:latest restart: always depends_on: - db environment: url: https://blog.computingpost.com database__client: mysql database__connection__host: db database__connection__user: root database__connection__password: StrongDBPassword database__connection__database: ghost volumes: - /opt/ghost_content:/var/lib/ghost/content db: image: mysql:8.0 restart: always environment: MYSQL_ROOT_PASSWORD: StrongDBPassword volumes: - /opt/ghost_mysql:/var/lib/mysql nginx: build: context: ./nginx dockerfile: Dockerfile restart: always depends_on: - ghost ports: - "80:80" - "443:443" volumes: - /etc/letsencrypt/:/etc/letsencrypt/ - /opt/ghost_nginx:/etc/nginx/conf.d/
3. Create Persistent Volumes
We have several volumes defined in the YAML file. These volumes will be used to persist data for Ghost, MySQL, and Nginx.
Create the 3 directories as below.
sudo mkdir /opt/ghost_content sudo mkdir /opt/ghost_mysql sudo mkdir /opt/ghost_nginx
Set the right permissions:
sudo chmod 777 /opt/ghost_content sudo chmod 777 /opt/ghost_mysql sudo chmod 777 /opt/ghost_nginx
For RHEL-based systems, set SELinux in permissive mode for the paths to be accessible.
sudo setenforce 0 sudo sed -i 's/^SELINUX=.*/SELINUX=permissive/g' /etc/selinux/config
4. Create the NGINX Docker Image
The Ghost deployment depends on the customized NGINX image. This image will contain customized server block settings.
First issue SSL certificates for your domain name using Let’s Encrypt. The required packages can be installed using the commands:
##On RHEL 8/CentOS/Rocky Linux 8/Fedora sudo dnf install https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm sudo dnf install certbot python3-certbot-nginx ##On Debian/Ubuntu sudo apt install certbot python3-certbot-nginx
Stop nginx service
sudo systemctl stop nginx sudo systemctl disable nginx
Once installed, issue the SSL certificate using the command:
sudo certbot certonly --standalone -d blog.computingpost.com
After executing the above command successfully, you will have the generated certificates saved at /etc/letsencrypt/live/blog.computingpost.com/
$ ls -1 /etc/letsencrypt/live/blog.computingpost.com README cert.pem chain.pem fullchain.pem privkey.pem
Now create a default.conf file in the same nginx directory.
sudo vim /opt/ghost_nginx/default.conf
The file will; have the below content:
server listen 80; listen [::]:80; server_name blog.computingpost.com; # Useful for Let's Encrypt location /.well-known/acme-challenge/ root /opt/ghost_nginx; allow all; location / return 301 https://$host$request_uri; server listen 443 ssl http2; listen [::]:443 ssl http2; server_name blog.computingpost.com; ssl_protocols TLSv1.2; ssl_ciphers HIGH:!MEDIUM:!LOW:!aNULL:!NULL:!SHA; ssl_prefer_server_ciphers on; ssl_session_cache shared:SSL:10m; ssl_certificate /etc/letsencrypt/live/blog.computingpost.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/blog.computingpost.com/privkey.pem; location / proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-Proto https; proxy_pass http://ghost:2368;
Don’t forget to replace blog.computingpost.com with your correct Ghost CMS domain name.
5. Run Ghost CMS in Docker Containers.
With everything provisioned, we are set to run the containers. We will use the single command below to spin the containers:
docker-compose up -d
[+] Running 22/22 ⠿ ghost Pulled 14.4s ⠿ ae13dd578326 Pull complete 4.0s ⠿ a0bc29d3bc64 Pull complete 4.1s ⠿ 54a71a1d6cc6 Pull complete 5.4s ⠿ fa5afd073302 Pull complete 5.6s ⠿ 5878f59eaa39 Pull complete 5.7s ⠿ 753aec878444 Pull complete 5.8s ⠿ 4e282a0a2747 Pull complete 7.4s ⠿ 6128fb86f0f4 Pull complete 12.1s ⠿ 509f7cfac26d Pull complete 12.2s ⠿ db Pulled 8.6s ⠿ a4b007099961 Pull complete 1.5s ⠿ e2b610d88fd9 Pull complete 1.5s ⠿ 38567843b438 Pull complete 1.7s ⠿ 5fc423bf9558 Pull complete 1.7s ⠿ aa8241dfe828 Pull complete 1.8s ⠿ cc662311610e Pull complete 2.4s ⠿ 9832d1192cf2 Pull complete 2.5s ⠿ 3f242378e320 Pull complete 2.5s ⠿ cc65503c0186 Pull complete 6.2s ⠿ ce8944d50437 Pull complete 6.3s ⠿ 597d59a9a424 Pull complete 6.4s Sending build context to Docker daemon 609B Step 1/2 : FROM nginx:latest
Once started, verify if the containers are running correctly.
$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 108f6be14df0 ghost_nginx "/docker-entrypoint.…" 8 seconds ago Up 7 seconds 0.0.0.0:80->80/tcp, :::80->80/tcp, 0.0.0.0:443->443/tcp, :::443->443/tcp ghost-nginx-1 2c8ed0b4a2c1 ghost:latest "docker-entrypoint.s…" 8 seconds ago Restarting (2) Less than a second ago ghost-ghost-1 750987dc3f70 mysql:8.0 "docker-entrypoint.s…" 8 seconds ago Up 7 seconds 3306/tcp, 33060/tcp ghost-db-1
6. Access the Ghost CMS web UI
Now we can proceed and access the Ghost CMS interface using the URL https://domain_name/ghost
Create an account, provide the required user and password credentials. You also need to set the blog title.
Now in the Ghost Admin tab, you can create your new blog as below.
You can as well make configurations such as the site’s theme e.t.c. Write the content in the new blog and publish it.
Once published, you can view the blog as below.
You can also manage the blogs in the below dashboard.
Make more settings for your site in the settings tab.
7. Manage the Ghost CMS containers.
Since we assigned the
restart: always flag in the YAML, you do not need to manually start the containers on system reboot.
To update Ghost, use the commands below.
docker-compose down docker-compose pull && docker-compose up -d
To renew your Let’s Encrypt Certificate, you need to create a Cron Job as below.
sudo crontab -e
Add the below line replacing where required.
0 23 * * * certbot certonly -n --webroot -w /opt/ghost_nginx -d blog.computingpost.com --deploy-hook='docker exec ghost_nginx_1 nginx -s reload'
You can as well test the job using the dry run command below:
sudo bash -c "certbot certonly -n --webroot -w /opt/ghost_nginx -d blog.computingpost.com --deploy-hook='docker exec ghost_nginx_1 nginx -s reload'"
If the certificate hasn’t expired, you will get this output:
Saving debug log to /var/log/letsencrypt/letsencrypt.log Certificate not yet due for renewal - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Certificate not yet due for renewal; no action taken. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
We have successfully gone through how to run Ghost CMS in Docker Containers. Now proceed and create professional online blogs with Ghost. Feel free to share any feedback in the comments.