Step-by-Step Guide: Installing Ghost CMS with Docker on Ubuntu 20.04

Ghost is a modern, open-source blogging platform designed to create sleek, professional websites. Introduced in 2013 as a streamlined alternative to the increasingly complex WordPress, Ghost leverages JavaScript and Node.js for its operations.

This step-by-step guide illustrates the installation of Ghost CMS using Docker on an Ubuntu 20.04 server. Additionally, we’ll employ Nginx as a reverse proxy and secure the setup with a Let’s Encrypt SSL certificate.

Prerequisites

  • Ubuntu 20.04 server instance.
  • A non-root user with sudo privileges.
  • Ensure your system is up to date:
    $ sudo apt update 
    $ sudo apt upgrade

Step 1 – Configure UFW Firewall

Ensure that your firewall is protecting your server by utilizing Ubuntu’s Uncomplicated Firewall (UFW).

First, verify the firewall status:

$ sudo ufw status

If it reads “inactive,” allow SSH connections to maintain connectivity:

$ sudo ufw allow OpenSSH

Permit traffic on HTTP and HTTPS ports:

$ sudo ufw allow 80
$ sudo ufw allow 443

Activate the firewall:

$ sudo ufw enable

Verify it’s active:

$ sudo ufw status

Step 2 – Install Certbot and Obtain the SSL Certificate

Using Certbot, we can automate the configuration of free SSL certificates from Let’s Encrypt. Update snap:

$ sudo snap install core 
$ sudo snap refresh core

Remove any older installations of Certbot:

$ sudo apt remove certbot

Then, install Certbot using Snap:

$ sudo snap install --classic certbot

Create a symbolic link for Certbot:

$ sudo ln -s /snap/bin/certbot /usr/bin/certbot

Generate your SSL certificate:

$ sudo certbot certonly --standalone -d example.com

Step 3 – Install Docker and Docker Compose

Uninstall outdated Docker versions if present:

$ sudo apt remove docker docker-engine docker.io containerd runc

Install required packages:

$ sudo apt install apt-transport-https ca-certificates curl gnupg lsb-release

Set up Docker’s GPG key and repository:

$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
$ echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

Now, proceed to install Docker:

$ sudo apt update
$ sudo apt install docker-ce docker-ce-cli containerd.io

Verify Docker installation:

$ sudo docker run hello-world

Allow non-root users to run Docker commands:

$ sudo usermod -aG docker ${USER}

Install Docker Compose:

sudo curl -L "https://github.com/docker/compose/releases/download/1.28.6/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
$ sudo chmod +x /usr/local/bin/docker-compose

Verify Docker Compose installation:

$ docker-compose --version

Step 4 – Install Ghost

The Ghost setup consists of three components: Ghost itself, a database like MySQL, and Nginx. These will be orchestrated through Docker Compose.

Create Docker Compose File

In your home directory, create a new directory for Ghost:

$ mkdir ghost && cd ghost

Create and edit a docker-compose.yml file:

$ nano docker-compose.yml

Populate it with this content, substituting placeholders accordingly:

version: '3.3'
services:

  ghost:
    image: ghost:latest
    restart: always
    depends_on:
      - db
    environment:
      url: https://example.com
      database__client: mysql
      database__connection__host: db
      database__connection__user: ghost
      database__connection__password: ghostdbpass
      database__connection__database: ghostdb
    volumes:
      - /home/<username>/ghost/content:/var/lib/ghost/content

  db:
    image: mariadb:latest
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: your_mysql_root_password
      MYSQL_USER: ghost
      MYSQL_PASSWORD: ghostdbpass
      MYSQL_DATABASE: ghostdb
    volumes:
      - /home/<username>/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/
      - /usr/share/nginx/html:/usr/share/nginx/html

Create necessary directories for bind mounts:

$ cd ~/ghost
$ mkdir content mysql
$ sudo mkdir -p /usr/share/nginx/html

Create the Nginx Docker Image

Create a directory for Nginx:

$ mkdir nginx

Create a Dockerfile and ghost.conf for configuring Nginx:

$ touch nginx/Dockerfile
$ touch nginx/ghost.conf

Edit the Dockerfile:

FROM nginx:latest
RUN rm /etc/nginx/conf.d/default.conf
COPY ghost.conf /etc/nginx/conf.d

Edit the ghost.conf, replacing example.com:

server {
  listen 80;
  listen [::]:80;
  server_name example.com;
  location /.well-known/acme-challenge/ { root /usr/share/nginx/html; allow all; }
  location / { return 301 https://$server_name$request_uri; }
}

server {
  listen 443 ssl http2;
  listen [::]:443 ssl http2;
  server_name example.com;
    
  access_log /var/log/nginx/ghost.access.log;
  error_log /var/log/nginx/ghost.error.log;
  client_max_body_size 20m;

  ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

  location / {
    proxy_set_header Host $http_host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-Proto https;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_pass http://ghost:2368;
  }
}

Step 5 – Run the Site

From the ghost directory, initiate the Ghost service:

$ docker-compose up -d

Check your blog by visiting https://example.com. If it doesn’t appear immediately, monitor the logs:

$ docker-compose down
$ docker-compose up

Step 6 – Complete Setup

Finalize your Ghost blog by accessing https://example.com/ghost, where you’ll configure your admin account.

Step 7 – Update Ghost

Keeps your Ghost installation current by continuing to update your Docker images:

$ docker-compose down
$ docker-compose pull && docker-compose up -d

Step 8 – Renew your Let’s Encrypt SSL Certificate

Automate certificate renewal with a cron job:

$ sudo crontab -e

Add this:

0 23 * * * certbot certonly -n --webroot -w /usr/share/nginx/html -d example.com --deploy-hook='docker exec ghost_nginx_1 nginx -s reload'

Test using dry-run:

$ sudo bash -c "certbot certonly -n --webroot --dry-run -w /usr/share/nginx/html -d example.com --deploy-hook='docker exec ghost_nginx_1 nginx -s reload'"

Conclusion

Congratulations on setting up Ghost CMS on your Ubuntu server using Docker. For questions or feedback, leave a comment below.

FAQ

What is Ghost CMS?

Ghost CMS is an open-source platform used for building and managing blogs and online publications. It is known for its speed and simplicity.

Why use Docker for installing Ghost?

Using Docker provides a consistent environment for running applications, simplifies the deployment process, and allows easy scalability.

How frequently should I update Ghost CMS?

Regular updates are recommended to keep your platform secure and take advantage of the latest features.

What if I encounter issues with the Ghost setup?

Check Docker logs for errors using docker-compose up and refer to Ghost’s documentation and forums for troubleshooting tips.