Skip to main content

Command Palette

Search for a command to run...

Simplifying Ghost Setup with Docker: My Journey from Complexity to Clarity

Updated
4 min read
Simplifying Ghost Setup with Docker: My Journey from Complexity to Clarity

The Initial Challenge

When I decided to set up Ghost for my blog, I thought it would be straightforward. I had some Docker experience, and Ghost seemed like a popular, well-documented platform. Little did I know I was about to embark on a debugging journey that would teach me valuable lessons about complexity vs simplicity.

The Complex Setup That Failed

I started with what seemed like a comprehensive Ghost development environment, a monorepo setup with 15+ services. Here's what I initially tried to run:

The Docker Compose File has 317 lines. What I started with was a complex monorepo setup for services:

server:           Ghost Server (2GB RAM)
admin:            Ghost Admin (Port 4200)
combined:         Combined Ghost (Multiple ports)
mysql:            MySQL 8.4.5 (Port 3306)
redis:            Redis 7.0 (Port 6379)
caddy:            Reverse Proxy (Ports 80, 443)
mailhog:          Email Testing (Ports 1025, 8025)
analytics:        Analytics Service (Port 3000)
tinybird-local:   TinyBird Local (Port 7181)
Prometheus:       Prometheus (Port 9090)
Grafana:          Grafana (Port 3000)
pushgateway:      Push Gateway (Port 9091)
portal:           Portal (Ports 4175-4176)
comments-ui:      Comments UI (Ports 7173-7174)
announcement-bar: Announcement Bar (Port 4177)
search:           Search (Port 4178)
signup-form:      Signup Form (Port 6174)
... and more!

Resource Requirements:

  • Memory: 8-16GB RAM

  • CPU: High-end development machine

  • Storage: Several GB for all the services

  • Network: 15+ port mappings

Major Issues I Encountered

  1. SSH_AUTH_SOCK Error: Invalid interpolation format for "volumes" service "server": invalid mount config for type "bind": Bind source path does not exist: /tmp/ssh-agent

What I Tried: Setting the SSH_AUTH_SOCK environment variable

  1. Memory Issues - Admin Killed Error: Admin service killed due to memory constraints. Ghost admin container exiting with code 137

What I Tried:

  • Increasing Docker memory limits

  • Closing other applications

  1. Theme Errors Theme error: Could not load theme. Failed to build admin assets

  2. Port Conflicts Error: Port 3000 already in use. Cannot bind to port 4200

  3. Build Failures ERROR: failed to solve: process "/bin/sh -c yarn install" did not complete successfully: exit code 1

The Solution: Simplicity Wins

After hours of debugging, I made a crucial decision: Start simple, then scale up.

Instead of trying to run a full development environment, I switched to the official Ghost Docker image. Here's what changed everything:

The Simple Setup That Works

My working solution - Clean and simple services:

version: '3.8'

services:
  ghost:
    image: ghost:latest  # Official Ghost image
    restart: always
    ports:
      - "2368:2368"
    environment:
      database__client: mysql
      database__connection__host: mysql
      database__connection__user: root
      database__connection__password: yourpassword
      database__connection__database: ghost
      url: http://localhost:2368
      NODE_ENV: development
    volumes:
      - ghost_content:/var/lib/ghost/content
    depends_on:
      mysql:
        condition: service_healthy
    deploy:
      resources:
        limits:
          memory: 512M

  mysql:
    image: mysql:8.0
    restart: always
    ports:
      - "3307:3306"
    environment:
      MYSQL_ROOT_PASSWORD: yourpassword
      MYSQL_DATABASE: ghost
    volumes:
      - mysql_data:/var/lib/mysql
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      timeout: 20s
      retries: 10

  adminer:
    image: adminer:latest
    restart: always
    ports:
      - "8080:8080"

volumes:
  ghost_content:
  mysql_data:

Result: From 317 lines to 62 lines, from 15+ services to 3 services, from 8-16GB RAM to 1.5GB RAM.

Step-by-Step Working Setup

Here's exactly how I got Ghost running:

Step 1: Clean Slate Remove all previous containers and images docker-compose down -v docker system prune -a

Step 2: Create the Simple Docker Compose. yml Create a new docker-compose.yml with the simple setup above, touch docker-compose.yml. Copy the working configuration

Step 3: Start the Services Start Ghost with the new setup: docker-compose up -d

Check if everything is running with docker-compose ps

Total Setup Time: 2 minutes Memory Usage: ~1.5GB Success Rate: 100%

Lessons Learned

  1. Start Simple, Scale Later Mistake: Trying to run a full development environment when I just needed Ghost. Lesson: Use official images first, then customise when you understand the system

  2. Official Images Are Your Friend Mistake: Assuming custom builds are better. Lesson: Official Docker images are tested, stable, and maintained

  3. Resource Management Matters Mistake: Ignoring memory and CPU requirements. Lesson: Always check your system resources before running complex setups

  4. Debugging Skills Are Essential Mistake: Not systematically debugging each error. Lesson: Break down problems, test one component at a time

Next Steps: Contributing to Open Source

Now that I have a stable Ghost setup, I've created a learning path to eventually contribute to Ghost as an open source project:

Final Thoughts

This experience taught me that simplicity is not the enemy of power. My current Ghost setup gives me:

  • All the features I need for blogging

  • A stable, reliable environment

  • Room to learn and grow

  • A foundation for future development

Sometimes the best solution is not the most complex one. Start with what works, understand it completely, then add complexity when you actually need it.

Have you faced similar challenges setting up development environments? Share your experiences in the comments below!