Hands-On Example of Docker Compose: Setting Up a Local Ghost Blog with Docker Compose

Learn how to set up a local Ghost blog with this hands-on Docker Compose tutorial. Follow step-by-step instructions to configure containers for the Ghost blog and a MySQL database, enabling you to test themes, plugins, and settings in a safe local environment before going live!

Hands-On Example of Docker Compose: Setting Up a Local Ghost Blog with Docker Compose

Introduction

Today, I want to talk about running a real-world application in containers using Docker Compose. Last week, I published a post on how to use a docker-compose.yml file and setting up local environments directly on your computer, "Using Docker Compose to Manage Multi-Container Networks". I also published an article about "Managing Multi-Container Setups with Docker Compose".

Now, let’s discuss how to use Docker Compose for real application examples, not just theoretical talking. Since I use Ghost for my blog and occasionally need to make changes to themes or configurations, it’s helpful to see how these changes look before applying them to the live site. For this purpose, I want to create a local development environment for testing.

Set it up

We’ll set up Docker Compose for Ghost and use it to test various changes locally. First, we’ll run a single container for the Ghost blog itself. To do this, we’ll locate the official Docker container for Ghost (latest version) on Docker Hub. After running it, we’ll notice that the container needs a database connection and additional configuration to function properly.

docker run --name ghost-blog -p 2368:2368 ghost:latest

Open a browser and navigate to http://localhost:2368 to check the blog.

  • Explanation:
    • --name ghost-blog: Names the container for easy identification.
    • -p 2368:2368: Maps port 2368 of the container to port 2368 on your host machine.
    • ghost:latest: Specifies the Ghost image to use.

From the Ghost container log:

[2024-12-05 17:54:17] INFO Ghost is running in production...
[2024-12-05 17:54:17] INFO Your site is now available on http://localhost:2368/
[2024-12-05 17:54:17] INFO Ctrl+C to shut down
[2024-12-05 17:54:17] INFO Ghost server started in 2.146s
[2024-12-05 17:54:18] ERROR connect ECONNREFUSED 127.0.0.1:3306

connect ECONNREFUSED 127.0.0.1:3306

"Unknown database error"

Error ID:
    500

Error Code:
    ECONNREFUSED

----------------------------------------

Error: connect ECONNREFUSED 127.0.0.1:3306
    at /var/lib/ghost/versions/5.103.0/node_modules/knex-migrator/lib/database.js:57:19
    at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1555:16)

[2024-12-05 17:54:18] WARN Ghost is shutting down
[2024-12-05 17:54:18] WARN Ghost has shut down
[2024-12-05 17:54:18] WARN Your site is now offline
[2024-12-05 17:54:18] WARN Ghost was running for a few seconds

Next, we’ll add a database container since Ghost requires a database to store its content. It supports MySQL, SQLite, and others. We’ll use MySQL for this setup. Looking at the Ghost Docker Hub page. Find the corresponding Docker image of MySQL, and use it for our setup.

Run the following command to start a MySQL container:

docker run --name ghost-db -e MYSQL_ROOT_PASSWORD=root -e MYSQL_DATABASE=ghost -p 3306:3306 -d mysql:latest
  • Explanation:
    • --name ghost-db: Names the database container.
    • -e MYSQL_ROOT_PASSWORD=root: Sets the root password for MySQL.
    • -e MYSQL_DATABASE=ghost: Creates a database named ghost.
    • -p 3306:3306: Maps port 3306 to the host for database access.
    • -d: Runs the container in detached mode.

Since managing multiple containers manually can be cumbersome, we’ll write a docker-compose.yml file to simplify the setup and configuration process. The file will define two services: one for the Ghost blog and one for the database. For now, we’ll keep it simple without adding volumes or custom networks.

To manage the Ghost blog and the database together, create a docker-compose.yml file in your project directory with the following content:

version: '3.8'

services:
  ghost-blog:
    image: ghost:latest
    ports:
      - "2368:2368"
    environment:
      database__client: mysql
      database__connection__host: ghost-db
      database__connection__user: root
      database__connection__password: root
      database__connection__database: ghost
    depends_on:
      - ghost-db

  ghost-db:
    image: mysql:latest
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: ghost
    ports:
      - "3306:3306"

We’ll configure the database container to initialize with the correct settings and tell the Ghost container how to connect to the database. Finally, we’ll test the setup to ensure everything runs as expected.

Run the following command to start both containers:

docker-compose up
  • Explanation:
    • docker-compose up: Starts all services defined in the docker-compose.yml file.

Verify the Setup

  1. Open your browser and navigate to http://localhost:2368 to access the Ghost blog.

Check the logs for any issues:

docker-compose logs

Preventing Data Loss with Docker Volumes

By default, any data generated or stored by your containers is ephemeral—it disappears when the containers are stopped or removed. This is especially problematic for applications like Ghost and MySQL, where blog content and database data must persist between container restarts. To prevent data loss, we can use Docker volumes to store this data outside of the container's lifecycle.

For our Ghost blog setup, we’ll create volumes for:

  1. The Ghost blog content (e.g., themes, images, settings).
  2. The MySQL database data.

Updating the docker-compose.yml File

Modify your docker-compose.yml file to include volumes for both services. Here's the updated configuration:

version: '3.8'

services:
  ghost-blog:
    image: ghost:latest
    ports:
      - "2368:2368"
    environment:
      database__client: mysql
      database__connection__host: ghost-db
      database__connection__user: root
      database__connection__password: root
      database__connection__database: ghost
    depends_on:
      - ghost-db
    volumes:
      - ghost-content:/var/lib/ghost/content  # Persist Ghost blog content

  ghost-db:
    image: mysql:latest
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: ghost
    ports:
      - "3306:3306"
    volumes:
      - mysql-data:/var/lib/mysql  # Persist MySQL database data

volumes:
  ghost-content:  # Named volume for Ghost content
  mysql-data:     # Named volume for MySQL data

Explanation:

  • Ghost Blog Volume (ghost-content):
    • The /var/lib/ghost/content directory inside the Ghost container stores themes, images, and other content. By mapping this directory to a volume (ghost-content), any changes made to themes or content will be saved persistently.
  • MySQL Volume (mysql-data):
    • The /var/lib/mysql directory inside the MySQL container holds the database files. Mapping this to a volume (mysql-data) ensures that all blog data stored in the database is preserved.
  • Global Volumes Section:
    • The volumes section at the bottom of the file defines the named volumes ghost-content and mysql-data. These volumes are created and managed by Docker.

Customize and Test

  • Make changes to the theme or configuration files.
  • Test the blog locally to verify your updates before applying them to the live site.

Conclusion

With this setup, you can easily run a local Ghost blog using Docker Compose. This environment provides a safe space to test themes, plugins, and configurations, ensuring that your live blog remains unaffected.

Feel free to extend this setup by adding volumes for data persistence or custom networks for advanced use cases.

Read next

Managing Multi-Container Setups with Docker Compose

Docker Compose is a powerful tool that simplifies multi-container orchestration, allowing developers to define, manage, and run multiple containers in a consistent and scalable way. By defining your application’s services, networks, and volumes in a single YAML file (docker-compose.yml).

Using Docker Compose for Local Development Environments

Docker Compose offers an effective solution for managing local development environments by allowing developers to define, run, and manage multi-container applications with ease. By using Docker Compose, you can ensure that your application runs in the same environment.

Creating a docker-compose.yml File

Docker Compose is a powerful tool that simplifies the process of defining and running multi-container Docker applications through the use of a docker-compose.yml file. This YAML configuration file allows developers to describe the services, networks, and volumes that their application requires