{
  "schema_version": 1,
  "generated_at": "2026-10-23T00:00:00.000Z",
  "canonical_url": "https://openlogs.fyi/tools/ghost",
  "slug": "ghost",
  "name": "Ghost",
  "tagline": "Publishing platform for blogs and paid newsletters, run with Docker Compose.",
  "replaces": {
    "summary": "Paid newsletter and blog publishing services, such as Substack and Medium.",
    "products": [
      {
        "slug": "medium",
        "name": "Medium",
        "vendor": "Medium",
        "url": "https://openlogs.fyi/alternatives/medium"
      },
      {
        "slug": "substack",
        "name": "Substack",
        "vendor": "Substack",
        "url": "https://openlogs.fyi/alternatives/substack"
      }
    ]
  },
  "stack": [
    {
      "kind": "language",
      "name": "TypeScript"
    },
    {
      "kind": "database",
      "name": "MySQL"
    }
  ],
  "deployment_methods": [
    "docker_compose"
  ],
  "requirements": {
    "min_ram_mb": null,
    "min_cpu_cores": null,
    "source_url": null
  },
  "license": {
    "spdx": "MIT",
    "class": "osi",
    "source_url": "https://github.com/TryGhost/Ghost/blob/db90cf0f34cc09b88e3ea17ab2aa9fb5997467ec/LICENSE"
  },
  "business_model": {
    "value": "unknown",
    "notes": "The README at the pinned commit does not state a business model, so it stays unknown.",
    "source_url": "https://github.com/TryGhost/Ghost/blob/db90cf0f34cc09b88e3ea17ab2aa9fb5997467ec/README.md",
    "checked_at": "2026-10-22"
  },
  "difficulty": {
    "value": "hard",
    "reason": "9 services (1 database or broker: mysql), 9 volume mounts, 4 env values to set, no elevated host access. Heuristic score 8 of 11: hard. A source checkout is needed, so the rating is at least medium."
  },
  "repository": {
    "owner": "TryGhost",
    "name": "Ghost",
    "url": "https://github.com/TryGhost/Ghost"
  },
  "compose_check": {
    "status": "config_ok",
    "validated_at": "2026-10-22",
    "summary": "config_ok on 2026-10-22"
  },
  "recipe": {
    "version": 1,
    "source_path": "compose.yml",
    "source_repo": "TryGhost/ghost-docker",
    "source_commit_sha": "fcaf12ff693b0d1fe522d7b144c0d9102aae9e9f",
    "source_kind": "packaging_repo",
    "source_url": "https://github.com/TryGhost/ghost-docker/blob/fcaf12ff693b0d1fe522d7b144c0d9102aae9e9f/compose.yml"
  },
  "install_steps": [
    {
      "order": 1,
      "kind": "prerequisite",
      "language": "text",
      "label": "Docker with the Compose plugin and git",
      "body": "A host with Docker, the Docker Compose plugin and git. This entry needs a checkout of the project repository.",
      "source_url": "https://github.com/TryGhost/Docs/blob/834eb3b22d653c58e4b2f514920ec6ff7b9e73c0/install/docker.mdx",
      "validation_status": "unvalidated"
    },
    {
      "order": 2,
      "kind": "shell",
      "language": "bash",
      "label": "Get the source at commit fcaf12f",
      "body": "git init ghost-docker\ncd ghost-docker\ngit remote add origin https://github.com/TryGhost/ghost-docker.git\ngit sparse-checkout init --cone\ngit sparse-checkout set caddy mysql-init tinybird\ngit fetch --depth 1 --filter=blob:none origin fcaf12ff693b0d1fe522d7b144c0d9102aae9e9f\ngit checkout FETCH_HEAD",
      "source_url": "https://github.com/TryGhost/Docs/blob/834eb3b22d653c58e4b2f514920ec6ff7b9e73c0/install/docker.mdx",
      "validation_status": "unvalidated"
    },
    {
      "order": 3,
      "kind": "compose",
      "language": "yaml",
      "label": "The compose file in the checkout: compose.yml (shown here for reading)",
      "body": "---\n# yaml-language-server: $schema=https://raw.githubusercontent.com/compose-spec/compose-spec/main/schema/compose-spec.json\nservices:\n  caddy:\n    image: caddy:2.10.2-alpine@sha256:953131cfea8e12bfe1c631a36308e9660e4389f0c3dfb3be957044d3ac92d446\n    restart: always\n    ports:\n      - \"${HTTP_PORT:-80}:80\"\n      - \"${HTTPS_PORT:-443}:443\"\n    environment:\n      DOMAIN: ${DOMAIN:?DOMAIN environment variable is required}\n      ADMIN_DOMAIN: ${ADMIN_DOMAIN:-}\n      ACTIVITYPUB_TARGET: ${ACTIVITYPUB_TARGET:-https://ap.ghost.org}\n    volumes:\n      - ./caddy:/etc/caddy\n      - caddy_data:/data\n      - caddy_config:/config\n    depends_on:\n      - ghost\n    networks:\n      - ghost_network\n\n  ghost:\n    # Do not alter this without updating the Tinybird Sync container as well\n    image: ghost:${GHOST_VERSION:-6-alpine}\n    restart: always\n    # This is required to import current config when migrating\n    env_file:\n      - .env\n    environment:\n      NODE_ENV: production\n      url: https://${DOMAIN:?DOMAIN environment variable is required}\n      admin__url: ${ADMIN_DOMAIN:+https://${ADMIN_DOMAIN}}\n      database__client: mysql\n      database__connection__host: db\n      database__connection__user: ${DATABASE_USER:-ghost}\n      database__connection__password: ${DATABASE_PASSWORD:?DATABASE_PASSWORD environment variable is required}\n      database__connection__database: ghost\n      tinybird__tracker__endpoint: https://${DOMAIN:?DOMAIN environment variable is required}/.ghost/analytics/api/v1/page_hit\n      tinybird__adminToken: ${TINYBIRD_ADMIN_TOKEN:-}\n      tinybird__workspaceId: ${TINYBIRD_WORKSPACE_ID:-}\n      tinybird__sync_auth_key: ${TINYBIRD_SYNC_AUTH:-}\n      tinybird__tracker__datasource: analytics_events\n      tinybird__stats__endpoint: ${TINYBIRD_API_URL:-https://api.tinybird.co}\n    volumes:\n      - ${UPLOAD_LOCATION:-./data/ghost}:/var/lib/ghost/content\n    depends_on:\n      db:\n        condition: service_healthy\n      tinybird-sync:\n        condition: service_completed_successfully\n        required: false\n      tinybird-deploy:\n        condition: service_completed_successfully\n        required: false\n      activitypub:\n        condition: service_started\n        required: false\n    networks:\n      - ghost_network\n\n  db:\n    image: mysql:8.0.44@sha256:f37951fc3753a6a22d6c7bf6978c5e5fefcf6f31814d98c582524f98eae52b21\n    restart: always\n    expose:\n      - \"3306\"\n    environment:\n      MYSQL_ROOT_PASSWORD: ${DATABASE_ROOT_PASSWORD:?DATABASE_ROOT_PASSWORD environment variable is required}\n      MYSQL_USER: ${DATABASE_USER:-ghost}\n      MYSQL_PASSWORD: ${DATABASE_PASSWORD:?DATABASE_PASSWORD environment variable is required}\n      MYSQL_DATABASE: ghost\n      MYSQL_MULTIPLE_DATABASES: activitypub\n    volumes:\n      - ${MYSQL_DATA_LOCATION:-./data/mysql}:/var/lib/mysql\n      - ./mysql-init:/docker-entrypoint-initdb.d\n    healthcheck:\n      test: mysqladmin ping -p$$MYSQL_ROOT_PASSWORD -h 127.0.0.1\n      interval: 1s\n      start_period: 30s\n      start_interval: 10s\n      retries: 120\n    networks:\n      - ghost_network\n\n  traffic-analytics:\n    image: ghost/traffic-analytics:1.0.498@sha256:22aeb87aaf288128edb11c9da40e50098e84261d2666050dc4a4892f66d554b9\n    restart: always\n    expose:\n      - \"3000\"\n    volumes:\n      - traffic_analytics_data:/data\n    environment:\n      NODE_ENV: production\n      PROXY_TARGET: ${TINYBIRD_API_URL:-https://api.tinybird.co}/v0/events\n      SALT_STORE_TYPE: ${SALT_STORE_TYPE:-file}\n      SALT_STORE_FILE_PATH: /data/salts.json\n      TINYBIRD_TRACKER_TOKEN: ${TINYBIRD_TRACKER_TOKEN:-}\n      TINYBIRD_SYNC_AUTH: ${TINYBIRD_SYNC_AUTH:-}\n      LOG_LEVEL: debug\n    profiles: [analytics]\n    networks:\n      - ghost_network\n\n  activitypub:\n    image: ghcr.io/tryghost/activitypub:1.2.15@sha256:24b48f68837f01b7adec5eaf8519af504189c441656e9c12ef0976d7953f8935\n    restart: always\n    expose:\n      - \"8080\"\n    volumes:\n      - ${UPLOAD_LOCATION:-./data/ghost}:/opt/activitypub/content\n    environment:\n      # See https://github.com/TryGhost/ActivityPub/blob/main/docs/env-vars.md\n      NODE_ENV: production\n      MYSQL_HOST: db\n      MYSQL_USER: ${DATABASE_USER:-ghost}\n      MYSQL_PASSWORD: ${DATABASE_PASSWORD:?DATABASE_PASSWORD environment variable is required}\n      MYSQL_DATABASE: activitypub\n      LOCAL_STORAGE_PATH: /opt/activitypub/content/images/activitypub\n      LOCAL_STORAGE_HOSTING_URL: https://${DOMAIN}/content/images/activitypub\n    depends_on:\n      db:\n        condition: service_healthy\n      activitypub-migrate:\n        condition: service_completed_successfully\n    profiles: [activitypub]\n    networks:\n      - ghost_network\n\n  # Supporting Services\n\n  tinybird-login:\n    build:\n      context: ./tinybird\n      dockerfile: Dockerfile\n    working_dir: /home/tinybird\n    command: /usr/local/bin/tinybird-login\n    volumes:\n      - tinybird_home:/home/tinybird\n      - tinybird_files:/data/tinybird\n    profiles: [analytics]\n    networks:\n      - ghost_network\n    tty: false\n    restart: no\n\n  tinybird-sync:\n    # Do not alter this without updating the Ghost container as well\n    image: ghost:${GHOST_VERSION:-6-alpine}\n    command: >\n      sh -c \"\n        if [ -d /var/lib/ghost/current/core/server/data/tinybird ]; then\n          rm -rf /data/tinybird/*;\n          cp -rf /var/lib/ghost/current/core/server/data/tinybird/* /data/tinybird/;\n          echo 'Tinybird files synced into shared volume.';\n        else\n          echo 'Tinybird source directory not found.';\n        fi\n      \"\n    volumes:\n      - tinybird_files:/data/tinybird\n    depends_on:\n      tinybird-login:\n        condition: service_completed_successfully\n    networks:\n      - ghost_network\n    profiles: [analytics]\n    restart: no\n\n  tinybird-deploy:\n    build:\n      context: ./tinybird\n      dockerfile: Dockerfile\n    working_dir: /data/tinybird\n    command: >\n      sh -c \"\n        tb-wrapper --cloud deploy\n      \"\n    volumes:\n      - tinybird_home:/home/tinybird\n      - tinybird_files:/data/tinybird\n    depends_on:\n      tinybird-sync:\n        condition: service_completed_successfully\n    profiles: [analytics]\n    networks:\n      - ghost_network\n    tty: true\n\n  activitypub-migrate:\n    image: ghcr.io/tryghost/activitypub-migrations:1.2.15@sha256:c6c6f9160b39bfcdb1fad0a7fca8bac05ad45a7393290a2e9cdcc4b6f1fd00be\n    environment:\n      MYSQL_DB: mysql://${DATABASE_USER:-ghost}:${DATABASE_PASSWORD:?DATABASE_PASSWORD environment variable is required}@tcp(db:3306)/activitypub\n    networks:\n      - ghost_network\n    depends_on:\n      db:\n        condition: service_healthy\n    profiles: [activitypub]\n    restart: no\n\nvolumes:\n  caddy_data:\n  caddy_config:\n  tinybird_files:\n  tinybird_home:\n  traffic_analytics_data:\n\nnetworks:\n  ghost_network:\n",
      "source_url": "https://github.com/TryGhost/ghost-docker/blob/fcaf12ff693b0d1fe522d7b144c0d9102aae9e9f/compose.yml",
      "validation_status": "config_ok"
    },
    {
      "order": 4,
      "kind": "shell",
      "language": "bash",
      "label": "Copy .env.example to .env",
      "body": "cp .env.example .env",
      "source_url": "https://github.com/TryGhost/Docs/blob/834eb3b22d653c58e4b2f514920ec6ff7b9e73c0/install/docker.mdx",
      "validation_status": "unvalidated"
    },
    {
      "order": 5,
      "kind": "shell",
      "language": "bash",
      "label": "Copy caddy/Caddyfile.example to caddy/Caddyfile",
      "body": "cp caddy/Caddyfile.example caddy/Caddyfile",
      "source_url": "https://github.com/TryGhost/Docs/blob/834eb3b22d653c58e4b2f514920ec6ff7b9e73c0/install/docker.mdx",
      "validation_status": "unvalidated"
    },
    {
      "order": 6,
      "kind": "note",
      "language": "text",
      "label": "What the compose file check ran with",
      "body": "The compose file check ran in a checkout of TryGhost/ghost-docker at fcaf12ff693b0d1fe522d7b144c0d9102aae9e9f (a sparse checkout: the top-level files and the folders caddy, mysql-init, tinybird), after .env.example was copied to .env and caddy/Caddyfile.example was copied to caddy/Caddyfile. The Docker install guide tells the reader to clone the ghost-docker repository, copy .env.example to .env and the Caddyfile example to caddy/Caddyfile, edit the domain and passwords, then run docker compose pull and docker compose up -d.",
      "source_url": "https://github.com/TryGhost/Docs/blob/834eb3b22d653c58e4b2f514920ec6ff7b9e73c0/install/docker.mdx",
      "validation_status": "unvalidated"
    },
    {
      "order": 7,
      "kind": "shell",
      "language": "bash",
      "label": "Start it",
      "body": "docker compose up -d",
      "source_url": "https://github.com/TryGhost/Docs/blob/834eb3b22d653c58e4b2f514920ec6ff7b9e73c0/install/docker.mdx",
      "validation_status": "unvalidated"
    },
    {
      "order": 8,
      "kind": "verify",
      "language": "text",
      "label": "Open the app",
      "body": "Open http://localhost:80. The compose file publishes host port 80 for the caddy service. The docs say to open the domain that is set as DOMAIN in .env. Caddy publishes ports 80 and 443.",
      "source_url": "https://github.com/TryGhost/ghost-docker/blob/fcaf12ff693b0d1fe522d7b144c0d9102aae9e9f/compose.yml",
      "validation_status": "unvalidated"
    }
  ],
  "notes": [
    {
      "kind": "gotcha",
      "title": "Some services build from source",
      "body": "These services have a build section and no image: tinybird-login, tinybird-deploy. They need the project source next to the compose file.",
      "source_url": "https://github.com/TryGhost/ghost-docker/blob/fcaf12ff693b0d1fe522d7b144c0d9102aae9e9f/compose.yml",
      "checked_at": "2026-10-22"
    },
    {
      "kind": "gotcha",
      "title": "The compose file publishes host ports",
      "body": "Host ports: 80 (caddy), 443 (caddy). A port without a host address listens on all host network interfaces.",
      "source_url": "https://github.com/TryGhost/ghost-docker/blob/fcaf12ff693b0d1fe522d7b144c0d9102aae9e9f/compose.yml",
      "checked_at": "2026-10-22"
    },
    {
      "kind": "gotcha",
      "title": "The compose file mounts host folders",
      "body": "Host paths mounted into containers: ./caddy at /etc/caddy (caddy), ${UPLOAD_LOCATION at -./data/ghost} (ghost), ${MYSQL_DATA_LOCATION at -./data/mysql} (db), ./mysql-init at /docker-entrypoint-initdb.d (db), ${UPLOAD_LOCATION at -./data/ghost} (activitypub).",
      "source_url": "https://github.com/TryGhost/ghost-docker/blob/fcaf12ff693b0d1fe522d7b144c0d9102aae9e9f/compose.yml",
      "checked_at": "2026-10-22"
    }
  ],
  "signals": {
    "captured_at": "2026-10-22",
    "stars": 55588,
    "forks": 11995,
    "open_issues": 224,
    "last_push": "2026-10-22",
    "days_since_push": 0,
    "latest_release": "v6.72.0",
    "latest_release_date": "2026-10-20",
    "days_since_release": 2
  },
  "links": {
    "html": "https://openlogs.fyi/tools/ghost",
    "markdown": "https://openlogs.fyi/tools/ghost.md",
    "json": "https://openlogs.fyi/tools/ghost.json"
  },
  "updated_at": "2026-10-22"
}
