Ctrl K
ComunidadGuías…

compose.dev.yaml

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
name: ghost-dev

services:
  mysql:
    image: mysql:8.4.11@sha256:0f51e63e537a1f176423fa03ef559aabe6fa4372a53160a44bc7e75da7d15364
    container_name: ghost-dev-mysql
    # Shared by every checkout's Ghost and DB test runs, which outgrow MySQL's default 151 connections
    command: --innodb-buffer-pool-size=1G --innodb-log-buffer-size=500M --innodb-change-buffer-max-size=50 --innodb-flush-log-at-trx_commit=0 --innodb-flush-method=O_DIRECT --log-error-verbosity=1 --max-connections=1000
    ports:
      - '3306:3306'
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-root}
      MYSQL_DATABASE: ${MYSQL_DATABASE:-ghost_dev}
      MYSQL_USER: ghost
      MYSQL_PASSWORD: ghost
    volumes:
      - mysql-data:/var/lib/mysql
    healthcheck:
      test:
        [
          'CMD',
          'mysql',
          '-h',
          '127.0.0.1',
          '-uroot',
          '-p${MYSQL_ROOT_PASSWORD:-root}',
          '-e',
          'SELECT 1',
        ]
      interval: 1s
      retries: 120
      timeout: 5s
      start_period: 10s
      start_interval: 1s

  redis:
    image: redis:7.4@sha256:2e3eb11462c4e3759e6972678dd051d150ff54b1e60714b3c33b701260172a90
    container_name: ghost-dev-redis
    command: ['redis-server', '--loglevel', 'warning']
    ports:
      - '6379:6379'
    volumes:
      - redis-data:/data
    healthcheck:
      test:
        - CMD
        - redis-cli
        - --raw
        - incr
        - ping
      interval: 1s
      retries: 120

  mailpit:
    image: axllent/mailpit@sha256:9b3a463be15354c0f0eba91697d80e3e0d8a5623ef0e627cacc836bc17fd5231
    container_name: ghost-dev-mailpit
    # Tags each message with the SMTP username, which `pnpm dev` sets to the checkout's name
    command: ['--quiet', '--smtp-auth-accept-any', '--smtp-auth-allow-insecure', '--tags-username']
    ports:
      - '1025:1025' # SMTP server
      - '8025:8025' # Web interface
      - '8026:8025' # Web interface (for e2e tests)
    healthcheck:
      interval: 1s
      retries: 30

  # Main development Ghost instance
  # Additional instances can be created programmatically via E2E GhostManager
  # `pnpm dev` runs Ghost and the dev servers on the host; these two services back
  # `pnpm dev:docker` and the modes built on it, and e2e dev mode.
  ghost-dev:
    profiles: ['docker-dev']
    build:
      context: ./
      dockerfile: docker/ghost-dev/Dockerfile
    container_name: ghost-dev
    working_dir: /home/ghost/ghost
    command: ['pnpm', 'dev']
    # The entrypoint drains Ghost before exiting; give that longer than Docker's 10s
    stop_grace_period: 45s
    volumes:
      # Mount the backend source as whole-directory mounts instead of
      # enumerating each server-graph workspace package. `pnpm dev` runs in
      # ghost, and pnpm's verify-deps-before-run only checks ghost's
      # own dependency closure — which is fully installed in the root
      # /home/ghost/node_modules baked into the image and never mounted over —
      # so the non-server packages these dirs also expose (e.g. koenig-lexical)
      # fall outside that closure and don't trigger a
      # workspace repair. Because root node_modules is never mounted,
      # linux-built native modules survive; each package's local node_modules
      # holds only relative symlinks that re-resolve into the root store.
      # verify-deps-before-run=false is set below as belt-and-suspenders in
      # case a future pnpm widens that check back to the whole workspace.
      - ./ghost:/home/ghost/ghost
      - ./koenig:/home/ghost/koenig
      - ./configs:/home/ghost/configs
      - ./packages:/home/ghost/packages
      # Mount specific content subdirectories to preserve themes/adapters from source
      - ghost-dev-data:/home/ghost/ghost/content/data
      - ghost-dev-images:/home/ghost/ghost/content/images
      - ghost-dev-media:/home/ghost/ghost/content/media
      - ghost-dev-files:/home/ghost/ghost/content/files
      - ghost-dev-logs:/home/ghost/ghost/content/logs
      - shared-config:/mnt/shared-config:ro
    environment:
      NODE_ENV: development
      NODE_TLS_REJECT_UNAUTHORIZED: '0'
      DEBUG: ${DEBUG:-}
      pnpm_config_verify_deps_before_run: 'false'
      database__client: mysql2
      database__connection__host: mysql
      database__connection__user: root
      database__connection__password: ${MYSQL_ROOT_PASSWORD:-root}
      database__connection__database: ${MYSQL_DATABASE:-ghost_dev}
      server__host: 0.0.0.0
      server__port: 2368
      # Apply Ghost(Pro)'s public query parameter allowlist in local development
      queryParameterFiltering__enabled: '${QUERY_PARAMETER_FILTERING_ENABLED:-true}'
      mail__transport: SMTP
      mail__options__host: mailpit
      mail__options__port: 1025
      # Redis cache (optional)
      adapters__cache__Redis__host: redis
      adapters__cache__Redis__port: 6379
      # Apps under development run on the host machine, where `localhost` is not this
      # container: Ghost fetches their manifests through this alias instead
      apps__localhostAlias: host.docker.internal
    # Docker Desktop and OrbStack provide host.docker.internal and forward it to the host's
    # loopback. On Linux it needs this, and it reaches the docker bridge rather than
    # loopback: an app that only listens on 127.0.0.1 (Vite's default) is not reachable
    # through it, so bind the app to 0.0.0.0 while developing against this container.
    extra_hosts:
      - 'host.docker.internal:host-gateway'
    depends_on:
      mysql:
        condition: service_healthy
      redis:
        condition: service_healthy
      mailpit:
        condition: service_healthy
      stripe:
        condition: service_healthy
        required: false
    healthcheck:
      test:
        [
          'CMD',
          'node',
          '-e',
          "fetch('http://localhost:2368',{redirect:'manual'}).then(r=>process.exit(r.status<500?0:1)).catch(()=>process.exit(1))",
        ]
      timeout: 5s
      retries: 10
      start_period: 5s

  # Caddy reverse proxy for the main dev instance
  # Routes Ghost backend + proxies asset requests to host dev servers
  ghost-dev-gateway:
    profiles: ['docker-dev']
    build:
      context: ./docker/dev-gateway
      dockerfile: Dockerfile
    container_name: ghost-dev-gateway
    environment:
      ANALYTICS_PROXY_TARGET: ${ANALYTICS_PROXY_TARGET:-analytics:3000}
    ports:
      - '2368:80'
      - '80:80'
    extra_hosts:
      - 'host.docker.internal:host-gateway'
    volumes:
      # Mount Caddyfile for live config changes without rebuilding
      - ./docker/dev-gateway/Caddyfile:/etc/caddy/Caddyfile:ro
      # All workspace apps mounted read-only. Caddy file_server resolves
      # /srv/apps/<name>/umd dynamically for the public-app routes (see
      # the Caddyfile @public_app block). Replaces the per-app `vite preview`
      # sirv layer + avoids per-app bind mounts.
      - ./apps:/srv/apps:ro
    depends_on:
      ghost-dev:
        condition: service_healthy

  stripe:
    image: stripe/stripe-cli:latest@sha256:c8e5a48108fb4c1e83583912c85810e9ac3bc8db91070da3f915ac369ceecf31
    container_name: ghost-dev-stripe
    entrypoint: ['/entrypoint.sh']
    profiles: ['stripe']
    volumes:
      - ./docker/stripe/entrypoint.sh:/entrypoint.sh:ro
      - shared-config:/mnt/shared-config
    environment:
      - GHOST_URL=${GHOST_URL:-http://ghost-dev:2368}
      - STRIPE_SECRET_KEY=${STRIPE_SECRET_KEY:-}
    # `pnpm dev:stripe --listen` forwards to the Admin dev server on the host
    extra_hosts:
      - 'host.docker.internal:host-gateway'
    healthcheck:
      test: ['CMD', 'test', '-f', '/mnt/shared-config/.env.stripe']
      interval: 1s
      retries: 120

volumes:
  mysql-data:
  redis-data:
  shared-config:
  ghost-dev-data:
  ghost-dev-images:
  ghost-dev-media:
  ghost-dev-files:
  ghost-dev-logs:

networks:
  default:
    name: ghost_dev