Self-Hosting A Ghost Website

How to self-host a Ghost website using a Raspberry Pi, Docker, and Cloudflare

Self-Hosting A Ghost Website

When using open source software, such as Ghost, self-hosting can be a cost effective alternative to a paid service. Historically, when setting up a ghost website, I follow the full install via the docs, but this time I decided to switch things up and host via Docker.

Install Ubuntu

First, you can use the Raspberry Pi Imager to install Ubuntu on an SD Card. In the process, you specify the machine name, configure SSH, and connect to the network. Then, simply plug in the SD card and you're good to go.

Install Docker

Once the server's up and running, you can SSH into it and install docker following the docs. Essentially, you only need to run a few commands:

# Add Docker's official GPG key:
sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

# Add the repository to Apt sources:
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update

# Install the Docker packages
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

If everything worked, you should be able to test the hello-world image

sudo docker run hello-world

Finally, if you want docker to run on startup, you can run

sudo systemctl enable docker.service
sudo systemctl enable containerd.service

Setup Mailgun

Ghost requires Mailgun for sending emails and newsletters, so you can follow the documentation here for getting the basics set up.

Signing up for a flex / pay as you go account (instead of a monthly subscription) required a bit of a work around, but this post details how to accomplish it.

Create a Docker-Compose

You could run the docker image for Ghost on its own, but I decided to use a docker-compose instead, because I wanted to group multiple services together. Initially, I tried to use MySQL, but ran into an error that there was no image for the architecture.

Considering that my site is pretty small, and upgrading later should be fairly straight forward, I opted to use a SQLite database instead. My final docker-compose file looked like this:

version: '3'

services:
  ghost:
    image: ghost:latest
    restart: always
    environment:
      - database__client=sqlite3
      - database__connection__filename=/var/lib/ghost/content/data/ghost.db
      - NODE_ENV=production
      - url={Website_Url}
      - mail__transport=SMTP
      - mail__options__service=Mailgun
      - mail__options__host=smtp.mailgun.org
      - mail__options__port=25
      - mail__options__secure=false
      - mail__options__auth__user={Mailgun_User}
      - mail__options__auth__pass={Mailgun_Password}
      - mail__from={No_Reply_Email}
    ports:
      - "2368:2368"
    volumes:
      - ./content:/var/lib/ghost/content

Here, I'm

  • exposing port 2368 of the container
  • configuring mailgun
  • using a volume to store all of the content (for easy access)

Configure Cloudflare

Before starting the container, we need a way to expose the site to the internet. To do so, I decided to use a Cloudflare Zero Trust tunnel.

From the Zero Trust -> Networks -> Tunnels tab in Cloudflare, you can click the Create a Tunnel button which then gives you the option to install a connector.

Once the connector's installed, you can direct a host name to a local port on the machine that has the connector installed. For example, this allows directing traffic from https://kevinwilliams.dev to localhost:2368 without exposing or forwarding any ports on the router.

Wrapping up

Now that Mailgun and Cloudflare are setup, you can start the docker container with docker compose up. Then, navigate to your domain, create an account, and import your content or make any configuration changes.

ℹ️
Since I created the docker-compose-yml on my laptop, I had to copy it to the server with scp ./docker-compose.yml {user}@{ip}:{directory}