Unleash the Ghost: Docker-Powered Blog Hosting on Raspberry Pi

Unleash the Ghost: Docker-Powered Blog Hosting on Raspberry Pi
Photo by Stefan Cosma on Unsplash

If you're looking to host a blog on your Raspberry Pi, Ghost is a great option. With its simplicity and focus on providing the best writing and publishing experience, Ghost is a popular choice for bloggers. In this guide, we'll walk you through the process of setting up a Ghost blog on your Raspberry Pi using Docker. Docker allows for easy deployment and management of containers, making it the perfect tool for hosting your blog.

Requirements

Before getting started, make sure you have the following:

  • Raspberry Pi with Raspberry Pi OS (64-bit) installed
  • Docker installed on your Raspberry Pi

Installing Docker on Raspberry Pi

If you haven't already installed Docker on your Raspberry Pi, follow these steps to get it up and running:

  1. Open the terminal on your Raspberry Pi.
  2. Download and run the Docker installation script by running the following command:
curl -sSL https://get.docker.com | sh
  1. After the installation is complete, add your user to the Docker group by running the following command:
sudo usermod -aG docker $USER
  1. Log out and log back in to apply the changes.

Setting up Docker Compose

Docker Compose doesn’t come installed with Docker. Installing Docker Compose on a Raspberry Pi requires a slightly different approach compared to a standard Linux machine because of the Pi's ARM architecture.

  1. First, we'll install required dependencies:
sudo apt-get install -y libffi-dev libssl-dev python3 python3-pip
  1. Now, install Docker Compose using pip:
sudo apt-get install -y libffi-dev libssl-dev python3 python3-pip
  1. To confirm the successful installation of Docker Compose:
docker-compose --version

Setting up Ghost with Docker Compose

To start your Ghost blog, we will use Docker Compose to configure and launch the necessary Docker containers. Follow these steps to set up your Ghost blog using Docker Compose:

  1. Create a new directory for your Ghost blog:
mkdir ghost-blog
  1. Navigate to the newly created directory:
cd ghost-blog
  1. Create a new file named docker-compose.yml
nano docker-compose.yml
  1. In the docker-compose.yml file, paste the following configuration:
version: "3.5"

services:
  nginx-proxy:
    container_name: ghost_nginx-proxy
    image: nginxproxy/nginx-proxy
    networks:
      - proxynet
    ports:
      - "80:80"
      - "443:443"
    labels:
      - "com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy=1"
    volumes:
      - /var/run/docker.sock:/tmp/docker.sock:ro
      - ./nginx-conf/my_proxy.conf:/etc/nginx/conf.d/my_proxy.conf:ro
      - ./nginx/certs:/etc/nginx/certs
      - ./nginx/vhost.d:/etc/nginx/vhost.d:ro
      - ./nginx/html:/usr/share/nginx/html:ro
    restart: always
    deploy:
      resources:
        limits:
          cpus: "2"
          memory: 1024M

  nginx-proxy-letsencrypt:
    image: jrcs/letsencrypt-nginx-proxy-companion
    networks:
      - proxynet
    environment:
      - NGINX_PROXY_CONTAINER=ghost_nginx-proxy
    volumes:
      - ./nginx/certs:/etc/nginx/certs:rw
      - ./nginx/vhost.d:/etc/nginx/vhost.d:rw
      - ./nginx/html:/usr/share/nginx/html:rw
      - /var/run/docker.sock:/var/run/docker.sock:ro
    restart: always
    deploy:
      resources:
        limits:
          cpus: "0.125"
          memory: 128M

  blog:
    image: ghost:5-alpine
    networks:
      - proxynet
    ports:
      - 2368:2368
    environment:
      - database__client=mysql
      - database__connection__host=blog-db
      - database__connection__user=root
      - database__connection__password=my_super_strong_password_here!
      - database__connection__database=blog_ghost
      - url=https://example.com
      - NODE_ENV=production
      # - NODE_ENV=development
      - VIRTUAL_HOST=example.com
      - VIRTUAL_PORT=2368
      - LETSENCRYPT_HOST=example.com
      - [email protected]
    volumes:
      - ./blog/content:/var/lib/ghost/content
    restart: always
    depends_on:
      - blog-db
    deploy:
      resources:
        limits:
          cpus: "2"
          memory: 4096M

  blog-db:
    image: mysql:8.0
    restart: always
    networks:
      - proxynet
    environment:
      MYSQL_ROOT_PASSWORD: my_super_strong_password_here!
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 30s
      timeout: 10s
      retries: 3
    volumes:
      - ./blog/db:/var/lib/mysql
    deploy:
      resources:
        limits:
          cpus: "2"
          memory: 4096M

networks:
  proxynet:
    name: proxy-network

To make your Ghost blog accessible from the Internet, we'll set up Nginx as a reverse proxy using the nginxproxy/nginx-proxy image. This service, named ghost_nginx-proxy, functions as an Nginx reverse proxy, listening on ports 80 and 443 for HTTP and HTTPS traffic. Its role is crucial in routing incoming traffic to the appropriate containers based on virtual hosts.

To ensure secure communication between your users and your Ghost blog, we'll enable HTTPS using Let's Encrypt certificates. The nginx-proxy-letsencrypt service, utilizing the jrcs/letsencrypt-nginx-proxy-companion image, plays a crucial role in this process. It automatically obtains and renews SSL certificates from Let's Encrypt, working seamlessly with the nginx-proxy service to provide secure connections.

To ensure robust functionality for your Ghost blog, we'll set up the blog-db service to handle the MySQL database. This service, utilizing the mysql:8.0 image, ensures the persistence of your blog data. It's configured to run consistently, with defined resources and health checks.

Now, let's focus on the blog service. This service uses the ghost:5-alpine image to run your Ghost blog. It listens on port 2368, enabling access to your content. Essential environment variables are set to establish the database connection, blog URL, and virtual host. The service is configured to restart always, ensuring continuous availability.

Make your Ghost blog accessible worldwide

To ensure that your Ghost blog can be accessed from anywhere in the world, it is necessary to configure your router by opening ports 80 and 443. Access your router's administration panel, which can usually be done through a web browser, and locate the port forwarding settings. Create rules that will redirect external traffic on ports 80 and 443 to the internal IP address of your Raspberry Pi, where the Ghost blog is running. By doing this, your router will be able to direct incoming HTTP and HTTPS requests to your Ghost blog, allowing users from around the world to access it.

In addition, for a more consistent access to your Raspberry Pi, consider setting up a static IP address. Look for the DHCP reservation or static IP assignment section in your router settings. Assign a static IP address to your Raspberry Pi based on its MAC address. This ensures that your Raspberry Pi will always receive the same IP address, even if it disconnects and reconnects to the network. Having a static IP address simplifies the process of accessing your Ghost blog and guarantees stable connections.

Setup your domain

To set up Cloudflare and associate your Raspberry Pi-hosted Ghost blog with a custom domain, follow these steps:

  1. Create a Cloudflare Account:
    Sign up for a Cloudflare account if you don't have one. Add your domain to Cloudflare and complete the setup process.
  2. Update Name Servers:
    Cloudflare will provide you with two name servers. Replace the name servers at your domain registrar with the Cloudflare ones. Wait for the changes to propagate.
  3. Configure 'A' Record:
    In the Cloudflare dashboard, go to the DNS settings. Add a new 'A' record:
    • Name or Domain: Enter the subdomain or leave blank for the root domain.
    • IPv4 Address: Input the static public IP address of your Raspberry Pi.
  4. SSL/TLS Settings (Optional):
    Customize SSL/TLS settings based on your blog's security needs. You can choose "Flexible" or "Full" depending on whether your Raspberry Pi has an SSL certificate.
  5. Verify and Test:
    Confirm that the DNS settings have propagated using Cloudflare's dashboard. Visit your custom domain, and it should now point to your Raspberry Pi blog.

Congratulations! Your Ghost blog is now accessible from the Internet. Simply replace example.com in the docker-compose.yml configuration with your own domain name.

Photo by Sincerely Media on Unsplash

Now it's time to start writing and sharing your thoughts with the world. Happy blogging!