How to Run Ghost on Docker: Complete Setup Guide

Arek Nawo
9 min read
platforms
TL;DR: Running Ghost CMS on Docker provides a flexible, reproducible blogging environment with easy scaling and deployment.
  1. Development Setup: Single command deployment using official Ghost Docker image on port 2368
  2. Production Setup: Docker Compose configuration with MariaDB database, persistent volumes, and stateful storage
  3. Security: SSL certificate via Certbot and Nginx reverse proxy for HTTPS access
This tutorial covers installation on Ubuntu 18.04+ with complete configuration files available on GitHub.

Ghost is one of the most popular open source blogging platforms. It boasts powerful features for almost everything related to blogging, such as editing, publishing, email newsletters, and offering paid subscriptions. Ghost offers pre-made themes that you can use to set up your blog quickly, but thanks to its extensive API, you can also use it as a headless CMS for your custom frontend.

You can install Ghost in several ways, one of which is Docker.

Deploying Ghost with Docker has many advantages. With Docker containers, you get a flexible and easily reproducible way to run your blog. It allows you to spin up another instance quickly when one fails or updates. On top of that, you get the same setup experience, regardless of if you’re in a development or production environment.

This article will guide you through the entire process of setting up a Ghost blog with Docker. You can find all configuration files from this tutorial in this GitHub repo.

Table of Contents

Ghost’s Features

Before diving in, let’s first explore some of Ghost’s features in depth.

Simplicity and Ease of Use

As you’ll see in a bit, Ghost is very simple to set up, especially with the pre-made Docker image. On top of that, the other parts of the Ghost experience are easy to grasp, too. Everything from the content editor to the settings panel has a clean, minimal design.

Editing and Publishing Experience

The Ghost editor is one of the best in the business. It provides everything you need to create excellent content, including:

  • Floating formatting menu.
  • Image, HTML, and other content blocks.
  • Media embeds for Twitter, YouTube, CodePen, and more.
  • Markdown shortcuts.
  • Reusable content snippets.

When you’re done writing, publishing is just a matter of providing some metadata and hitting the Publish button.

How to build a content engine.

Email Newsletters

In addition to blogging, Ghost can also be used for email newsletters. With its built-in email design editor, members list, email analytics, and Mailgun integrations, you get a feature-complete solution for running your own newsletter right from your blog.

Subscriptions and Memberships

Thanks to its Stripe integration, a custom API, and a member administration dashboard, Ghost has everything you need to implement paid subscriptions and monetize your blog. You can create anything from a straightforward donation system to paywalled content or premium newsletters.

Setting Up Ghost with Docker

This guide assumes you’re running Ubuntu 18.04 or newer. To run Ghost in Docker, a minimum of 1 GB of RAM and a decent CPU are recommended.

Installing Docker

Before installing Ghost, you’ll need to install Docker Engine and Docker Compose. Be sure to uninstall any previous versions of Docker Engine that might be installed on your system:

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

Use apt to install all packages required by Docker:

sudo apt-get update
sudo apt-get install \
    ca-certificates \
    curl \
    gnupg \
    lsb-release

Add Docker’s official GPG key to verify the integrity of the Docker packages:

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg

Set up Docker’s stable repository as a package download source:

echo \
  "deb [arch=$(dpkg --print-architecture) 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

Install Docker Engine and Docker Compose, then verify the installation:

sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose
docker --version
docker-compose --version

If everything was set up correctly, you should see version numbers for Docker Engine and Docker Compose in your terminal.

Running the Ghost Image

With Docker ready, you can now use it to download and run the Ghost image.

For Development

This is the easiest way to get the Ghost image up and running for development purposes. With this setup, the container is stateless, meaning all data will be lost if anything happens to it. Also, by default, the Ghost blog is using an SQLite database.

Run the Ghost image using the following command:

docker run -d --name my-ghost-blog -e url=http://localhost:2368 -p 2368:2368 ghost

This will create a container named my-ghost-blog using the official Ghost image provided by the Docker community. The Ghost blog will be accessible on port 2368.

While convenient and great for development purposes, this setup isn’t ready for production. For that, a bit more configuration is required.

For Production

Start by creating a dedicated directory, like my-ghost-blog, and a docker-compose.yml file inside it:

mkdir my-ghost-blog
cd my-ghost-blog
touch docker-compose.yml

Inside the file, define the configuration for Docker containers:

version: "3.3"
services:
  ghost:
    image: ghost:latest
    restart: always
    ports:
      - "2368:2368"
    depends_on:
      - db
    environment:
      url: http://localhost:2368
      database__client: mysql
      database__connection__host: db
      database__connection__user: ghost
      database__connection__password: ghostdbpass
      database__connection__database: ghostdb
    volumes:
      - /home/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/ghost/mysql:/var/lib/mysql

With this config, an additional MySQL database container will be created using a MariaDB image. Additionally, a dedicated volume is added for the Ghost blog to provide stateful storage.

Now, execute the configuration with docker-compose up -d. You can now see the Ghost blog on port 2368.

Ghost with default Casper theme

Setting Up Your Ghost Blog

To get started with Ghost, go to http://localhost:2368/ghost. This is where your admin panel will be located. Going there for the first time, you should see a setup screen like the below:

Ghost admin setup

Fill out the form, and you’ll be greeted with the dashboard.

Ghost dashboard

To create your first post, click the + button next to Posts, which will take you to the post editor.

Ghost editor

From inside the editor, you can write your blog post and set all of its metadata. When you’re done, click Publish to make your post public.

Exposing and Securing the Ghost Blog

Now, while the Ghost blog is up and running, there’s still some setup to be done on the backend. You’ll have to expose the app for outside access with a proxy like Nginx and secure it with an SSL certificate. This process will require a domain, which we’ll refer to as example.com.

Obtaining an SSL Certificate

To obtain an SSL certificate for your domain, you’ll first have to install the latest Certbot package with the Snapd package installer.

First, ensure Snapd is up-to-date and that no older versions of Certbot are installed:

sudo snap install core
sudo snap refresh core
sudo apt remove certbot

Next up, install Certbot and create a symlink to its installation path to ensure it’s accessible through the certbot command:

sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot

Finally, run the following command to generate and download the certificate for your domain:

sudo certbot certonly --standalone -d example.com

With the SSL certificate ready, you can move on to Nginx.

Creating Custom Nginx Docker Image

In this example, you’ll see how to run Nginx as a separate container with Docker Compose.

To get started, create a new nginx folder:

mkdir nginx
cd nginx

You have to alter the official Nginx image to include a custom Nginx config. To do so, create a file called ghost.conf to hold the custom Nginx config:

server {
  listen 80;
  listen [::]:80;
  server_name example.com;
  # Useful for Let's Encrypt
  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_protocols TLSv1.2 TLSv1.3;
  ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
  ssl_prefer_server_ciphers on;
  ssl_session_timeout 1d;
  ssl_session_cache shared:SSL:10m;

  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;
  }
}

This configuration exposes the Ghost app running in a separate container (at http://ghost:2368), and includes the necessary configuration for the SSL certificate to work correctly.

Now, create a new Dockerfile where you’ll alter the Nginx default image:

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

With the Dockerfile ready, you can now return to your Docker Compose configuration.

How to turn readers into customers.

Updating the Docker Compose Config

If your Ghost blog is still running, you should first stop it with the docker-compose down command. As the configuration has already had a volume attached, all the data will persist through the reset.

Update the docker-compose.yml file to add a separate Nginx container, and remove the ports property from the ghost container:

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/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/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

With that done, run the app with docker-compose up -d once again. If everything is set up correctly, you should see your Ghost blog under https://example.com.

Deployment Options

Thanks to Docker’s popularity and flexibility, there are tons of options to choose from when it comes to hosting your app. A well-designed containerized app can run everywhere from virtual private servers to dedicated Docker cloud environments or Kubernetes clusters.

Some of the most popular Docker deployment platforms include:

Conclusion

In this post, you’ve learned how to leverage the flexible and reproducible architecture allowed by Docker to set up your own Ghost blog. The only thing left now is to fill it with excellent technical content. That’s where Draft.dev steps in.

Draft.dev is a company that provides high-quality technical content for all your needs. Check out our website and schedule a call to get your technical blogging game going!

Frequently Asked Questions

Can I run Ghost CMS on Docker for free?

Yes, Ghost CMS is open source and free to run on Docker. You only pay for hosting infrastructure (like DigitalOcean, AWS, or your own server). The official Ghost Docker image is free to use, though Ghost Pro offers paid managed hosting if you prefer not to self-host.

What are the minimum system requirements for Ghost on Docker?

Ghost on Docker requires at least 1 GB of RAM and a modern CPU running Ubuntu 18.04 or newer. For production blogs with moderate traffic, 2 GB RAM and 2 CPU cores are recommended. You'll also need Docker Engine 20.10+ and Docker Compose 1.29+ installed.

How do I backup my Ghost Docker blog?

Back up your Ghost Docker blog by copying the persistent volumes defined in docker-compose.yml: the content directory (/var/lib/ghost/content) and MySQL data directory (/var/lib/mysql). Use docker-compose down to stop containers, then copy these directories to your backup location. Ghost also provides built-in export functionality in the admin panel.

Can I use Ghost as a headless CMS with Docker?

Yes, Ghost works excellently as a headless CMS with Docker. You can use Ghost's Content API to fetch posts and serve them through a custom frontend (React, Next.js, Vue, etc.). The Docker setup remains the same, but you'll configure your frontend to connect to Ghost's API endpoint.

How do I update Ghost when running on Docker?

Update Ghost Docker by pulling the latest image with 'docker-compose pull ghost', then restart containers with 'docker-compose up -d'. Your data persists in volumes, so updates are safe. Always backup before updating and review Ghost's changelog for breaking changes in major version upgrades.

What's the difference between Ghost Docker development and production setup?

Development setup uses a single stateless container with SQLite database, suitable for testing but loses data when restarted. Production setup uses Docker Compose with MariaDB database, persistent volumes for data retention, proper SSL certificates, and Nginx reverse proxy for security and performance.

Can I run multiple Ghost blogs on one Docker server?

Yes, you can run multiple Ghost instances on one server using Docker. Create separate directories for each blog with unique docker-compose.yml files, use different port mappings, and configure Nginx to route traffic based on domain names. Each instance needs its own database and content volumes.

How do I troubleshoot Ghost Docker connection issues?

Check container logs with 'docker-compose logs ghost' to identify errors. Common issues include incorrect database credentials in environment variables, missing volumes causing data loss, or port conflicts. Ensure MariaDB container is running before Ghost starts using the 'depends_on' configuration.

About the Author

Arek Nawo

Arek Nawo is a web developer, freelancer, and creator of CodeWrite.

Share this article:TwitterLinkedIn

Continue Reading

Explore our complete library of technical content marketing resources and developer relations insights.

View all posts

Want to learn more about how we work?