Ghost CMS is a popular open-source content management system that is specifically designed for bloggers and writers.

In this blog post, we’ll explore the pros and cons of Ghost CMS, compare it to WordPress, and highlight its unique features that make it a top choice for content creators. And of course, you will get to know how to use Ghost and create your Website for Free.

The Ghost CMS Project

Create and manage your content for free with ease using Ghost CMS

We’ll also show you how to install Ghost CMS with Docker and expose it safely to the internet with Cloudflare Tunnel.

Why using Ghost as CMS?

  • User-Friendly: Ghost CMS has a simple and intuitive user interface, making it easy for users to create and manage their content.

  • Customization: With a wide range of themes and plugins available, Ghost CMS provides ample customization options for users. The platform also has a built-in editor that supports Markdown and HTML, giving users more control over their content.

  • SEO-Friendly: Ghost CMS is designed with SEO in mind, with built-in optimization tools and the ability to create custom URLs and meta descriptions.

  • Speed: Ghost CMS is built to be fast, with a focus on minimalism and performance. This means that websites built on Ghost CMS load quickly, providing a better user experience.

Publish content, send newsletters & offer paid subscriptions to members

You should also be aware that 馃憞
  • Limited Functionality: While Ghost CMS is great for blogging and writing, it may not be the best choice for complex websites or e-commerce sites.

  • Smaller Community: Compared to platforms like WordPress, Ghost CMS has a smaller community and fewer resources available for support and troubleshooting.

Ghost versus Wordpress

While both Ghost CMS and WordPress are popular content management systems, there are some key differences between the two.

Ghost or WordPress? Which one is for me? 馃憞
  • User Interface: Ghost CMS has a simpler and more streamlined user interface, while WordPress has a more complex and customizable interface.

  • Focus: Ghost CMS is focused on blogging and writing, while WordPress can be used for a wider range of websites and applications.

  • Performance: Ghost CMS is built for speed and performance, while WordPress can be slower due to its more complex nature and reliance on plugins.

In my opinion, Ghost feels much more modern and responsive than Wordpress.

Ghost Themes

Before moving on, I am sure that you want to see how your website can look with Ghost.

Generally, you can have a quick look to Free themes in Ghost Official Page.

Some of my fav Ghost Themes 馃憞
  • Free theme alto in Ghost Official Page.
  • Free theme edge in Ghost Official Page.
  • Free theme headline in Ghost Official Page.

SelfHosting Ghost

The docker image that we are going to use to deploy Ghost:Ghost DockerHub.

Pre-Requisites!! Just Get Docker 馃悑馃憞

Important step and quite recommended for any SelfHosting Project - Get Docker Installed

It will be one command, this one, if you are in Linux:

apt-get update && sudo apt-get upgrade && curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh && docker version

And install also Docker-compose with:

apt install docker-compose -y

Ghost - Docker Compose

We just need this Docker-Compose file to deploy Ghost with the required SQL Database:

version: '3.1'

services:
  ghost:
    image: ghost:5-alpine
    container_name: ghostcontainer
    environment:
      database__client: mysql
      database__connection__host: dbghost
      database__connection__user: ghostuser
      database__connection__password: ghost_db_pass
      database__connection__database: ghost_database
      url: http://ghostcontainer:2368
    restart: always
    ports:
      - 2368:2368
    networks:
      - ghost_network
    volumes:
      - ghost_data:/var/lib/ghost/content
    depends_on:
      - dbghost

  dbghost:
    container_name: ghostdbcontainer
    image: mariadb:10.5 #mysql (but MariaDB is open source)
    restart: always
    environment:
      MYSQL_DATABASE: ghost_database
      MYSQL_USER: ghostuser
      MYSQL_PASSWORD: ghost_db_pass
      MYSQL_ROOT_PASSWORD: ubabuuuHaGba6nhX
    ports:
      - 3306:3306
    networks:
      - ghost_network
    volumes:
      - db_ghost_data:/var/lib/mysql

volumes:
  ghost_data:
  db_ghost_data:

networks:
  ghost_network:

After the deployment is completed, you will be able to check your working instance of Ghost at:

  • The site: http://ourlocalip:2368
  • The admin panel and setup: http://ourlocalip:2368/ghost/

You will see that Ghost is doing the magic in the background the first time you run it:

Ghost First Boot

And just after a little while, you will have everything ready:

Ghost Login UI

Ghost with NGINX and Docker Compose

If you are interested to exposed the service to the internet, you will want to run uptime kuma behind a NGINX proxy. Follow this steps:

version: '3.1'

services:

  ghost:
    image: ghost:5-alpine
    restart: always
    ports:
      - 8088:2368
    environment:
      # see https://ghost.org/docs/config/#configuration-options #make sure to change these!
      database__client: mysql
      database__connection__host: db
      database__connection__user: root
      database__connection__password: example
      database__connection__database: ghost
      # this url value is just an example, adjust it properly!
      url: https://subdomain.yourdomain.com
      # contrary to the default mentioned in the linked documentation, this image defaults to NODE_ENV=production (so development mode needs to be explicitly specified if desired)
      #NODE_ENV: development
    networks:
       - nginx_default  

  db:
    image: linuxserver/mariadb #mysql:8.0
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: example
networks:
    nginx_default:
       external: true        

IMPORTANT: make sure to change the default passwords in the configuration file before deploying and exposing it to the internet.

  • Step 3: Finish the setup in NGINX UI as covered in its guide.

Ghost with Cloudflare Tunnels

First, get familiar with the Cloudflare Docker setup, as covered during the guide to expose your services securely with Cloudflare Zero Trust Tunnel.

Once you have that ready, simply use the tunnel network that connects our matrix-synapse instance to the Cloudflare tunnel during the installation with docker-compose:

version: '3.1'

services:
  ghost:
    image: ghost:5-alpine
    container_name: ghostcontainer
    environment:
      database__client: mysql
      databaseconnectionhost: dbghost #should match the name of the service of mariadb
      databaseconnectionuser: ghostuser
      databaseconnectionpassword: ghost_db_pass
      databaseconnectiondatabase: ghost_database
      url: http://192.168.3.23:8018
      #url: http://ghostcontainer:8018
      #url: https://your_fantastic_domain.com #remember about https or images wont load while accesing the post      
    restart: always
    networks:
      - ghost_network
      - cloudflare_tunnel
    ports:
      - 8018:2368
    volumes:
      - ghost_data:/var/lib/ghost/content
    depends_on: 
      - dbghost

  dbghost:
    container_name: ghostdbcontainer
    image: mariadb:latest #10.5
    restart: always
    environment:
      MYSQL_DATABASE: ghost_database
      MYSQL_USER: ghostuser
      MYSQL_PASSWORD: ghost_db_pass
      MYSQL_ROOT_PASSWORD: another_strong_pass
    networks:
      - ghost_network
    volumes:
      - db_ghost_data:/var/lib/mysql

volumes:
  ghost_data:
  db_ghost_data:

networks:
  ghost_network:
  cloudflare_tunnel:
    external: true

Remember to go back to the One Dash Cloudflare UI:

  • Add a Public Hostname in the Cloudflare web UI
    • Select the desired subdomain, domain and path (where applicable)
    • Then, add proxy host:
      • If your yml does not have the container name, use the service name and the port: http://ghost-web-service:2368
      • If you have filled the container name, then: http://ghost_web_xyz:2368

After this step, your service is already protected with HTTPS and accesible through the internet in the desired subdomain.yourdomain.com


FAQ

What is GHOST Headless CMS?

You can use Ghost as CMS. with few SSG’s like: Gatsby, NextJS, Nuxt and Hexo.

How to Deploy Ghost with Podman?

You can use the following script to install Ghost and its DB with this Podman CLI:

#!/bin/bash

# Define your container names
GHOST_CONTAINER="my-ghost-blog"
DB_CONTAINER="my-ghost-db"

# Define your environment variables
GHOST_DB_HOST="dbghost"
GHOST_DB_USER="ghostuser"
GHOST_DB_PASSWORD="ghost_db_pass"
GHOST_DB_NAME="ghost_database"

# Define ports and volumes
GHOST_PORT=2368
DB_PORT=3306
GHOST_VOLUME="my-ghost-volume"
DB_VOLUME="my-db-volume"

# Create a Podman network if it doesn't exist
podman network exists my-network || podman network create my-network

# Deploy the MariaDB container
podman run -d --name "$DB_CONTAINER" \
    --network my-network \
    -e MYSQL_DATABASE="$GHOST_DB_NAME" \
    -e MYSQL_USER="$GHOST_DB_USER" \
    -e MYSQL_PASSWORD="$GHOST_DB_PASSWORD" \
    -e MYSQL_ROOT_PASSWORD="ubabuuuHaGba6nhX" \
    -v "$DB_VOLUME":/var/lib/mysql \
    -p "$DB_PORT":3306 \
    docker.io/library/mariadb:10.5

# Wait for the MariaDB container to start (adjust as needed)
sleep 10

# Deploy the Ghost container
podman run -d --name "$GHOST_CONTAINER" \
    --network my-network \
    -e database__client=mysql \
    -e database__connection__host="$GHOST_DB_HOST" \
    -e database__connection__user="$GHOST_DB_USER" \
    -e database__connection__password="$GHOST_DB_PASSWORD" \
    -e database__connection__database="$GHOST_DB_NAME" \
    -v "$GHOST_VOLUME":/var/lib/ghost/content \
    -p "$GHOST_PORT":2368 \
    docker.io/library/ghost:5-alpine

# Display container status
podman ps -a

echo "Ghost blog is now deployed and running with Podman."

How to Copy photos from Ghost container to local?

Connect to container + scp 馃憞

As easy as it gets, lets connect to the container and copy to the Host, then copy to other machine with scp if you need to:

docker exec -it container_name /bin/bash
docker cp container_name:/var/lib/ghost/content/images /home/some/folder
scp -r someuser@192.168.3.200:/home/some/folder /home/someotheruser/Desktop/someotherfolder