Skip to main content
Ghost is a modern, open-source publishing platform built specifically for blogging. Unlike heavier CMS platforms that bundle forums, e-commerce, and complex page builders, Ghost stays focused on writing and publishing — which makes it fast, lean, and straightforward to self-host.

Why Ghost?

Ghost has several practical advantages over alternatives like WordPress:
  • Lightweight — Ghost is purpose-built for blogging with no unnecessary features. This keeps memory usage low and makes it suitable for running on modest hardware or a small VPS.
  • Low resource consumption — the Node.js runtime and minimal feature set mean Ghost typically runs comfortably within 512 MB of RAM under normal load.
  • Fully customizable themes — Ghost uses a Handlebars-based theming system. You can build themes from scratch or modify existing ones with full control over markup and styles.
  • Extensible — Ghost supports webhooks, integrations, and a native API, making it easy to connect to external services or build custom workflows around your content.

Prerequisites

You need Docker and Docker Compose installed on your server. Verify both are available before proceeding:
docker --version
docker compose version

Setup

1

Create a project directory

Create a dedicated directory for your Ghost deployment. All data — including the database and uploaded content — will be stored here.
mkdir ghost-blog
cd ghost-blog
2

Create the Docker Compose file

Create a docker-compose.yml file in your project directory with the following contents. This configuration runs Ghost 5 alongside a MySQL 8.0 database.
services:
  db:
    image: mysql:8.0
    volumes:
      - ./mysql:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: PASSWORD
      MYSQL_DATABASE: ghost
  ghost:
    image: ghost:5
    restart: always
    ports:
      - '10002:2368'
    volumes:
      - ./content:/var/lib/ghost/content
    depends_on:
      - db
Ghost will be accessible on port 10002 of your host. The ./content volume persists your themes, images, and uploaded files across container restarts. The ./mysql volume persists your database.
Change MYSQL_ROOT_PASSWORD: PASSWORD to a strong, unique password before starting the containers. Using the default value exposes your database to unauthorized access if the port is ever reachable externally.
3

Start the containers

Start Ghost and MySQL in detached mode:
docker compose up -d
Docker will pull the required images on the first run. Once complete, Ghost will be available at http://your-server:10002.
4

Complete the Ghost setup wizard

Open your browser and navigate to http://your-server:10002/ghost. You will be prompted to create an admin account and configure your blog’s title and details. Complete the wizard to finish the initial setup.
5

Reverse proxy with nginx (optional)

To serve Ghost on a standard port (80/443) with a domain name, configure nginx as a reverse proxy in front of Ghost’s port 10002.
server {
    listen 80;
    server_name yourdomain.com;

    location / {
        proxy_pass http://127.0.0.1:10002;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}
After adding this configuration, reload nginx:
sudo nginx -s reload

Customizing your theme

Ghost’s theme system gives you complete control over your blog’s appearance. The active theme lives in ./content/themes/. You can duplicate the default casper theme as a starting point, edit the Handlebars templates and CSS, then activate your custom theme from the Ghost admin panel under Settings → Design.
Themes are hot-reloadable in development mode, and Ghost’s theme API is well-documented. You can also install community themes by dropping the theme folder into ./content/themes/ and activating it from the admin panel.

Updating Ghost

To update to a newer Ghost version, update the image tag in your docker-compose.yml (or pull the latest ghost:5 image) and restart:
docker compose pull
docker compose up -d
Always back up your ./content and ./mysql directories before upgrading.