[Self-Hosted] 개인용 블로그로 Ghost를 설치해보자

[Self-Hosted] 개인용 블로그로 Ghost를 설치해보자

나는 기록용 블로그로 Ghost를 사용 중이다.

Ghost에는 회원, 멤버십, 뉴스레터 등 다양한 기능들이 있다.

자세한 기능은 아래에서 확인할 수 있다.

Introduction - Ghost Developer Docs
Ghost is an open source, professional publishing platform built on a modern Node.js technology stack — designed for teams who need power, flexibility and performance.

Ghost Documentation


설치하기

Ghost는 도커 이미지와 구축 가이드를 공식적으로 지원한다.

How To Install Ghost With Docker (preview) - Ghost Developer Docs
Preview our new batteries-included tools for self-hosting Ghost using Docker Compose.

How To Install Ghost With Docker (preview)


필수 조건

  • 리눅스 기반 서버
  • 도커 20.10.13 이상
  • 도메인
  • 이메일
  • (선택적) 웹 분석을 위한 Tinybird 계정

repo 복제하기

git clone https://github.com/TryGhost/ghost-docker.git /opt/ghost && cd /opt/ghost

.env 작성하기

# Use the below flags to enable the Analytics or ActivityPub containers as well
# COMPOSE_PROFILES=analytics,activitypub

# Ghost domain
# Custom public domain Ghost will run on
DOMAIN=example.com

# Ghost Admin domain
# If you have Ghost Admin setup on a separate domain uncomment the line below and add the domain
# You also need to uncomment the corresponding block in your Caddyfile
# ADMIN_DOMAIN=

# Database settings
# All database settings must not be changed once the database is initialised
DATABASE_ROOT_PASSWORD=reallysecurerootpassword
# DATABASE_USER=optionalusername
DATABASE_PASSWORD=ghostpassword

# ActivityPub
# If you'd prefer to self-host ActivityPub yourself uncomment the line below
# ACTIVITYPUB_TARGET=activitypub:8080

# Tinybird configuration
# If you want to run Analytics, paste the output from `docker compose run --rm tinybird-login get-tokens` below
# TINYBIRD_API_URL=https://api.tinybird.co
# TINYBIRD_TRACKER_TOKEN=p.eyJxxxxx
# TINYBIRD_ADMIN_TOKEN=p.eyJxxxxx
# TINYBIRD_WORKSPACE_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

# Ghost configuration (https://ghost.org/docs/config/)

# SMTP Email (https://ghost.org/docs/config/#mail)
# Transactional email is required for logins, account creation (staff invites), password resets and other features
# This is not related to bulk mail / newsletter sending
mail__transport=SMTP
mail__options__host=smtp.example.com
mail__options__port=465
mail__options__secure=true
[email protected]
mail__options__auth__pass=1234567890

# Advanced customizations

# Force Ghost version
# You should only do this if you need to pin a specific version
# The update commands won't work
# GHOST_VERSION=6-alpine

# Port Ghost should listen on
# You should only need to edit this if you want to host
# multiple sites on the same server
# GHOST_PORT=2368

# Data locations
# Location to store uploaded data
UPLOAD_LOCATION=./data/ghost

# Location for database data
MYSQL_DATA_LOCATION=./data/mysql

https://github.com/TryGhost/ghost-docker/blob/main/.env.example

# Use the below flags to enable the Analytics or ActivityPub containers as well
# COMPOSE_PROFILES=analytics,activitypub

Ghost에는 트래픽 분석 서비스(analytics)와 내장 소셜 미디어(activitypub)가 있다.

두 기능은 선택적으로 사용할 수 있다.

# Ghost domain
# Custom public domain Ghost will run on
DOMAIN=example.com

자신이 가지고 있는 도메인으로 수정한다.

# Database settings
# All database settings must not be changed once the database is initialised
DATABASE_ROOT_PASSWORD=reallysecurerootpassword
# DATABASE_USER=optionalusername
DATABASE_PASSWORD=ghostpassword

PASSWORDopenssl rand -hex 32 명령어로 각각 생성하고 수정한다.

DATABASE_USER는 지정하지 않을 시 기본값은 ghost이다.

# ActivityPub
# If you'd prefer to self-host ActivityPub yourself uncomment the line below
# ACTIVITYPUB_TARGET=activitypub:8080

Caddy 컨테이너에서 사용할 환경 변수이다.

activitypub 컨테이너의 주소를 지정한다.

# Tinybird configuration
# If you want to run Analytics, paste the output from `docker compose run --rm tinybird-login get-tokens` below
# TINYBIRD_API_URL=https://api.tinybird.co
# TINYBIRD_TRACKER_TOKEN=p.eyJxxxxx
# TINYBIRD_ADMIN_TOKEN=p.eyJxxxxx
# TINYBIRD_WORKSPACE_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

방문자 수, 트래픽 등을 분석해주는 기능을 위해 필요하다.

사용하려면 Tinybird 계정이 필요하다.

처음에는 주석만 해제한다.

이후에 도커 컴포즈를 실행하여 나중에 수정한다.

# SMTP Email (https://ghost.org/docs/config/#mail)
# Transactional email is required for logins, account creation (staff invites), password resets and other features
# This is not related to bulk mail / newsletter sending
mail__transport=SMTP
mail__options__host=smtp.example.com
mail__options__port=465
mail__options__secure=true
[email protected]
mail__options__auth__pass=1234567890

작성자는 구글 이메일을 사용하였다.

mail__transport=SMTP
mail__options__host=smtp.gmail.com
mail__options__port=587
mail__options__secure=true
[email protected]
mail__options__auth__pass=1234567890

mail__options__auth__user는 자신의 지메일 아이디
mail__options__auth__pass는 자신의 지메일 비밀번호 (2차 인증은 앱 비밀번호)

# Force Ghost version
# You should only do this if you need to pin a specific version
# The update commands won't work
# GHOST_VERSION=6-alpine

Ghost 도커 이미지 태그를 지정할 수 있다.

# Port Ghost should listen on
# You should only need to edit this if you want to host
# multiple sites on the same server
# GHOST_PORT=2368

Ghost 도커 컨테이너 호스트 포트이다.

# Data locations
# Location to store uploaded data
UPLOAD_LOCATION=./data/ghost

# Location for database data
MYSQL_DATA_LOCATION=./data/mysql

Ghost와 MySQL 데이터를 저장할 위치이다.

필수 설정을 위해 CONFIG_LOCATION=./config/config.production.json을 추가한다.

아래는 작성자가 사용 중인 .env 내용이다.

# Use the below flags to enable the Analytics or ActivityPub containers as well
COMPOSE_PROFILES=analytics,activitypub

# Ghost domain
# Custom public domain Ghost will run on
DOMAIN=수정필요.com

# Database settings
# All database settings must not be changed once the database is initialised
DATABASE_ROOT_PASSWORD=수정필요
DATABASE_USER=ghost
DATABASE_PASSWORD=수정필요

# Tinybird configuration
# If you want to run Analytics, paste the output from `docker compose run --rm tinybird-login get-tokens` below
TINYBIRD_API_URL=수정필요
TINYBIRD_WORKSPACE_ID=수정필요
TINYBIRD_ADMIN_TOKEN=수정필요
TINYBIRD_TRACKER_TOKEN=수정필요

# Ghost configuration (https://ghost.org/docs/config/)

# SMTP Email (https://ghost.org/docs/config/#mail)
# Transactional email is required for logins, account creation (staff invites), password resets and other features
# This is not related to bulk mail / newsletter sending
mail__transport=SMTP
mail__options__host=smtp.gmail.com
mail__options__port=587
mail__options__secure=true
mail__options__auth__user=수정필요
mail__options__auth__pass=수정필요

# Advanced customizations

# Force Ghost version
# You should only do this if you need to pin a specific version
# The update commands won't work
GHOST_VERSION=6-alpine

# Port Ghost should listen on
# You should only need to edit this if you want to host
# multiple sites on the same server
GHOST_PORT=9090

# Data locations
# Location to store uploaded data
UPLOAD_LOCATION=./data/ghost

# Location to store config data
CONFIG_LOCATION=./config/config.production.json

# Location for database data
MYSQL_DATA_LOCATION=./data/mysql

# Location for analytics data
ANALYTICS_DATA_LOCATION=./data/tinybird/analytics

.env


config.production.json 작성하기

{
  "url": "https://수정필요.com",

  "database": {
    "client": "mysql",
    "connection": {
      "host": "수정필요",
      "port": 3306,
      "user": "ghost",
      "password": "수정필요",
      "database": "ghost"
    }
  },

  "mail": {
    "transport": "DIRECT",
  },

  "server": {
    "host": "0.0.0.0",
    "port": 2368
  },

  "logging": {
    "level": "info",
    "transports": ["stdout"]
  },

  "process": "systemd",

  "security": {
    "staffDeviceVerification": true
  },

  "paths": {
    "contentPath": "/var/lib/ghost/content"
  }
}

url, database, mail은 필수이다.

뉴스레터를 사용하려면 Mailgun 설정이 필요하다.

여기서는 다루지 않는다.

Configuration - Ghost Developer Docs
For self-hosted Ghost users, a custom configuration file can be used to override Ghost’s default behaviour. This provides you with a range of options to configure your publication to suit your needs.

Configuring with Mailgun

더 많은 설정은 아래에서 확인할 수 있다.

Configuration - Ghost Developer Docs
For self-hosted Ghost users, a custom configuration file can be used to override Ghost’s default behaviour. This provides you with a range of options to configure your publication to suit your needs.

Configuration


compose.yml 작성하기

---
# yaml-language-server: $schema=https://raw.githubusercontent.com/compose-spec/compose-spec/main/schema/compose-spec.json
services:
  caddy:
    image: caddy:2.10.0-alpine@sha256:e2e3a089760c453bc51c4e718342bd7032d6714f15b437db7121bfc2de2654a6
    restart: always
    ports:
      - "80:80"
      - "443:443"
    environment:
      DOMAIN: ${DOMAIN:?DOMAIN environment variable is required}
      ADMIN_DOMAIN: ${ADMIN_DOMAIN:-}
      ACTIVITYPUB_TARGET: ${ACTIVITYPUB_TARGET:-https://ap.ghost.org}
    volumes:
      - ./caddy:/etc/caddy
      - caddy_data:/data
      - caddy_config:/config
    depends_on:
      - ghost
    networks:
      - ghost_network

  ghost:
    # Do not alter this without updating the Tinybird Sync container as well
    image: ghost:${GHOST_VERSION:-6-alpine}
    restart: always
    expose:
      - "127.0.0.1:${GHOST_PORT:-2368}:2368"
    # This is required to import current config when migrating
    env_file:
      - .env
    environment:
      NODE_ENV: production
      url: https://${DOMAIN:?DOMAIN environment variable is required}
      admin__url: ${ADMIN_DOMAIN:+https://${ADMIN_DOMAIN}}
      database__client: mysql
      database__connection__host: db
      database__connection__user: ${DATABASE_USER:-ghost}
      database__connection__password: ${DATABASE_PASSWORD:?DATABASE_PASSWORD environment variable is required}
      database__connection__database: ghost
      tinybird__tracker__endpoint: https://${DOMAIN:?DOMAIN environment variable is required}/.ghost/analytics/api/v1/page_hit
      tinybird__adminToken: ${TINYBIRD_ADMIN_TOKEN:-}
      tinybird__workspaceId: ${TINYBIRD_WORKSPACE_ID:-}
      tinybird__tracker__datasource: analytics_events
      tinybird__stats__endpoint: ${TINYBIRD_API_URL:-https://api.tinybird.co}
    volumes:
      - ${UPLOAD_LOCATION:-./data/ghost}:/var/lib/ghost/content
    depends_on:
      db:
        condition: service_healthy
      tinybird-sync:
        condition: service_completed_successfully
        required: false
      tinybird-deploy:
        condition: service_completed_successfully
        required: false
      activitypub:
        condition: service_started
        required: false
    networks:
      - ghost_network

  db:
    image: mysql:8.0.42@sha256:4445b2668d41143cb50e471ee207f8822006249b6859b24f7e12479684def5d9
    restart: always
    expose:
      - "3306"
    environment:
      MYSQL_ROOT_PASSWORD: ${DATABASE_ROOT_PASSWORD:?DATABASE_ROOT_PASSWORD environment variable is required}
      MYSQL_USER: ${DATABASE_USER:-ghost}
      MYSQL_PASSWORD: ${DATABASE_PASSWORD:?DATABASE_PASSWORD environment variable is required}
      MYSQL_DATABASE: ghost
      MYSQL_MULTIPLE_DATABASES: activitypub
    volumes:
      - ${MYSQL_DATA_LOCATION:-./data/mysql}:/var/lib/mysql
      - ./mysql-init:/docker-entrypoint-initdb.d
    healthcheck:
      test: mysqladmin ping -p$$MYSQL_ROOT_PASSWORD -h 127.0.0.1
      interval: 1s
      start_period: 30s
      start_interval: 10s
      retries: 120
    networks:
      - ghost_network

  traffic-analytics:
    image: ghost/traffic-analytics:1.0.20@sha256:a72573d89457e778b00e9061422516d2d266d79a72a0fc02005ba6466e391859
    restart: always
    expose:
      - "3000"
    volumes:
      - traffic_analytics_data:/data
    environment:
      NODE_ENV: production
      PROXY_TARGET: ${TINYBIRD_API_URL:-https://api.tinybird.co}/v0/events
      SALT_STORE_TYPE: ${SALT_STORE_TYPE:-file}
      SALT_STORE_FILE_PATH: /data/salts.json
      TINYBIRD_TRACKER_TOKEN: ${TINYBIRD_TRACKER_TOKEN:-}
      LOG_LEVEL: debug
    profiles: [analytics]
    networks:
      - ghost_network

  activitypub:
    image: ghcr.io/tryghost/activitypub:1.1.0@sha256:39c212fe23603b182d68e67d555c6b9b04b1e57459dfc0bef26d6e4980eb04d1
    restart: always
    expose:
      - "8080"
    volumes:
      - ${UPLOAD_LOCATION:-./data/ghost}:/opt/activitypub/content
    environment:
      # See https://github.com/TryGhost/ActivityPub/blob/main/docs/env-vars.md
      NODE_ENV: production
      MYSQL_HOST: db
      MYSQL_USER: ${DATABASE_USER:-ghost}
      MYSQL_PASSWORD: ${DATABASE_PASSWORD:?DATABASE_PASSWORD environment variable is required}
      MYSQL_DATABASE: activitypub
      LOCAL_STORAGE_PATH: /opt/activitypub/content/images/activitypub
      LOCAL_STORAGE_HOSTING_URL: https://${DOMAIN}/content/images/activitypub
    depends_on:
      db:
        condition: service_healthy
      activitypub-migrate:
        condition: service_completed_successfully
    profiles: [activitypub]
    networks:
      - ghost_network

  # Suporting Services

  tinybird-login:
    build:
      context: ./tinybird
      dockerfile: Dockerfile
    working_dir: /home/tinybird
    command: /usr/local/bin/tinybird-login
    volumes:
      - tinybird_home:/home/tinybird
      - tinybird_files:/data/tinybird
    profiles: [analytics]
    networks:
      - ghost_network
    tty: false
    restart: no

  tinybird-sync:
    # Do not alter this without updating the Ghost container as well
    image: ghost:${GHOST_VERSION:-6-alpine}
    command: >
      sh -c "
        if [ -d /var/lib/ghost/current/core/server/data/tinybird ]; then
          rm -rf /data/tinybird/*;
          cp -rf /var/lib/ghost/current/core/server/data/tinybird/* /data/tinybird/;
          echo 'Tinybird files synced into shared volume.';
        else
          echo 'Tinybird source directory not found.';
        fi
      "
    volumes:
      - tinybird_files:/data/tinybird
    depends_on:
      tinybird-login:
        condition: service_completed_successfully
    networks:
      - ghost_network
    profiles: [analytics]
    restart: no

  tinybird-deploy:
    build:
      context: ./tinybird
      dockerfile: Dockerfile
    working_dir: /data/tinybird
    command: >
      sh -c "
        tb-wrapper --cloud deploy
      "
    volumes:
      - tinybird_home:/home/tinybird
      - tinybird_files:/data/tinybird
    depends_on:
      tinybird-sync:
        condition: service_completed_successfully
    profiles: [analytics]
    networks:
      - ghost_network
    tty: true

  activitypub-migrate:
    image: ghcr.io/tryghost/activitypub-migrations:1.1.0@sha256:b3ab20f55d66eb79090130ff91b57fe93f8a4254b446c2c7fa4507535f503662
    environment:
      MYSQL_DB: mysql://${DATABASE_USER:-ghost}:${DATABASE_PASSWORD:?DATABASE_PASSWORD environment variable is required}@tcp(db:3306)/activitypub
    networks:
      - ghost_network
    depends_on:
      db:
        condition: service_healthy
    profiles: [activitypub]
    restart: no

volumes:
  caddy_data:
  caddy_config:
  tinybird_files:
  tinybird_home:
  traffic_analytics_data:

networks:
  ghost_network:

https://github.com/TryGhost/ghost-docker/blob/main/compose.yml

공식 문서에서는 Caddy를 사용한다.

이 글에서 Caddy 사용법은 설명하지 않는다.

아래는 작성자가 사용하는 compose.yml 내용이다.

컨테이너 이름, 속성 순서, 네트워크, 볼륨 등을 수정하였다.

services:
  ghost:
    image: 'ghost:${GHOST_VERSION}'
    container_name: ghost
    hostname: ghost
    restart: always
    expose:
      - ${GHOST_PORT}:2368
    env_file:
      - .env
    volumes:
      - ${UPLOAD_LOCATION}:/var/lib/ghost/content
      - ${CONFIG_LOCATION}:/var/lib/ghost/config.production.json:ro
    environment:
      - TZ=Asia/Seoul
      - NODE_ENV=production
      - url=https://${DOMAIN}
      - database__client=mysql
      - database__connection__host=ghost-mysql
      - database__connection__user=${DATABASE_USER}
      - database__connection__password=${DATABASE_PASSWORD}
      - database__connection__database=ghost
      - tinybird__tracker__endpoint=https://${DOMAIN}/.ghost/analytics/api/v1/page_hit
      - tinybird__adminToken=${TINYBIRD_ADMIN_TOKEN}
      - tinybird__workspaceId=${TINYBIRD_WORKSPACE_ID}
      - tinybird__tracker__datasource=analytics_events
      - tinybird__stats__endpoint=${TINYBIRD_API_URL}
    networks:
      - docker
    depends_on:
      ghost-mysql:
        condition: service_healthy
      ghost-tinybird-sync:
        condition: service_completed_successfully
        required: false
      ghost-tinybird-deploy:
        condition: service_completed_successfully
        required: false
      ghost-activitypub:
        condition: service_started
        required: false

  ghost-mysql:
    image: 'mysql:8.0.42@sha256:4445b2668d41143cb50e471ee207f8822006249b6859b24f7e12479684def5d9'
    container_name: ghost-mysql
    hostname: ghost-mysql
    restart: always
    expose:
      - 3306
    env_file:
      - .env
    volumes:
      - ${MYSQL_DATA_LOCATION}:/var/lib/mysql
      - ./mysql-init:/docker-entrypoint-initdb.d
    environment:
      - TZ=Asia/Seoul
      - MYSQL_ROOT_PASSWORD=${DATABASE_ROOT_PASSWORD}
      - MYSQL_USER=${DATABASE_USER}
      - MYSQL_PASSWORD=${DATABASE_PASSWORD}
      - MYSQL_DATABASE=ghost
      - MYSQL_MULTIPLE_DATABASES=activitypub
    networks:
      - docker
    healthcheck:
      test: mysqladmin ping -p$$MYSQL_ROOT_PASSWORD -h 127.0.0.1
      interval: 1s
      start_period: 30s
      retries: 120

  ghost-traffic-analytics:
    profiles: [analytics]
    image: 'ghost/traffic-analytics:1.0.16@sha256:9bfdad7f32661f7fc2c0eb32bbf096d43e358ec416dffc7ca6df58185cb0cde3'
    container_name: ghost-traffic-analytics
    hostname: ghost-traffic-analytics
    restart: always
    expose:
      - 3000
    volumes:
      - ${ANALYTICS_DATA_LOCATION}:/data
    environment:
      - TZ=Asia/Seoul
      - NODE_ENV=production
      - PROXY_TARGET=${TINYBIRD_API_URL}/v0/events
      - SALT_STORE_TYPE=file
      - SALT_STORE_FILE_PATH=/data/salts.json
      - TINYBIRD_TRACKER_TOKEN=${TINYBIRD_TRACKER_TOKEN}
      - LOG_LEVEL=debug
    networks:
      - docker

  ghost-activitypub:
    profiles: [activitypub]
    image: 'ghcr.io/tryghost/activitypub:1.1.0@sha256:39c212fe23603b182d68e67d555c6b9b04b1e57459dfc0bef26d6e4980eb04d1'
    container_name: ghost-activitypub
    hostname: ghost-activitypub
    restart: always
    expose:
      - 8080
    env_file:
      - .env
    volumes:
      - ${UPLOAD_LOCATION}:/opt/activitypub/content
    environment:
      - TZ=Asia/Seoul
      - NODE_ENV=production
      - PORT=8080
      - MYSQL_HOST=ghost-mysql
      - MYSQL_USER=${DATABASE_USER}
      - MYSQL_PASSWORD=${DATABASE_PASSWORD}
      - MYSQL_DATABASE=activitypub
      - LOCAL_STORAGE_PATH=/opt/activitypub/content/images/activitypub
      - LOCAL_STORAGE_HOSTING_URL=https://${DOMAIN}/content/images/activitypub
    networks:
      - docker
    depends_on:
      ghost-mysql:
        condition: service_healthy
      ghost-activitypub-migrate:
        condition: service_completed_successfully

  # Supporting Services
  ghost-tinybird-login:
    profiles: [analytics]
    build:
      context: ./build
      dockerfile: Dockerfile
    container_name: ghost-tinybird-login
    restart: no
    working_dir: /home/tinybird
    command: /usr/local/bin/tinybird-login
    tty: false
    volumes:
      - ./data/tinybird/home:/home/tinybird
      - ./data/tinybird/files:/data/tinybird
    networks:
      - docker

  ghost-tinybird-sync:
    profiles: [analytics]
    # Do not alter this without updating the Ghost container as well
    image: 'ghost:${GHOST_VERSION}'
    container_name: ghost-tinybird-sync
    restart: no
    command: >
      sh -c "
        if [ -d /var/lib/ghost/current/core/server/data/tinybird ]; then
          rm -rf /data/tinybird/*;
          cp -rf /var/lib/ghost/current/core/server/data/tinybird/* /data/tinybird/;
          echo 'Tinybird files synced into shared volume.';
        else
          echo 'Tinybird source directory not found.';
        fi
      "
    volumes:
      - ./data/tinybird/files:/data/tinybird
    networks:
      - docker
    depends_on:
      ghost-tinybird-login:
        condition: service_completed_successfully

  ghost-tinybird-deploy:
    profiles: [analytics]
    build:
      context: ./build
      dockerfile: Dockerfile
    container_name: ghost-tinybird-deploy
    working_dir: /data/tinybird
    command: >
      sh -c "
        tb-wrapper --cloud deploy
      "
    tty: true
    volumes:
      - ./data/tinybird/home:/home/tinybird
      - ./data/tinybird/files:/data/tinybird
    networks:
      - docker
    depends_on:
      ghost-tinybird-sync:
        condition: service_completed_successfully

  ghost-activitypub-migrate:
    profiles: [activitypub]
    image: 'ghcr.io/tryghost/activitypub-migrations:1.1.0@sha256:b3ab20f55d66eb79090130ff91b57fe93f8a4254b446c2c7fa4507535f503662'
    container_name: ghost-activitypub-migrate
    hostname: ghost-activitypub-migrate
    restart: no
    env_file:
      - .env
    environment:
      - TZ=Asia/Seoul
      - MYSQL_DB=mysql://${DATABASE_USER}:${DATABASE_PASSWORD}@tcp(ghost-mysql:3306)/activitypub
    networks:
      - docker
    depends_on:
      ghost-mysql:
        condition: service_healthy

networks:
  docker:
    name: docker
    external: true

compose.yml

작성자의 compose.yml을 사용하려면 볼륨, 네트워크를 자신의 환경에 맞게 수정이 필요하다.

tinybird-logintinybird-deploybuild:가 필요하므로 처음에 복제한 저장소 안에 있는 tinybird 폴더를 지정해야 한다.


Caddyfile 작성하기

blog.uiram.com {
	encode gzip

	# Proxy analytics requests with any prefix (e.g. /.ghost/analytics/ or /blog/.ghost/analytics/)
	@analytics_paths path_regexp analytics_match ^(.*)/\.ghost/analytics(.*)$
	handle @analytics_paths {
		rewrite * {re.analytics_match.2}
		reverse_proxy ghost-traffic-analytics:3000
	}

	handle /.ghost/activitypub/* {
		reverse_proxy ghost-activitypub:8080
	}

	handle /.well-known/webfinger {
		reverse_proxy ghost-activitypub:8080
	}

	handle /.well-known/nodeinfo {
		reverse_proxy ghost-activitypub:8080
	}

	handle {
		reverse_proxy ghost:2368
	}
}

Caddyfile

reverse_proxy URL:PORT는 자신의 환경에 맞게 수정한다.


실행하기

docker compose up -d을 실행한다.

도커 컨테이너 로그를 확인하고 INFO Ghost booted in XX.XXXs가 뜨면

https://도메인.com/ghost으로 접속한다.

접속이 잘 된다면 안내에 따라 설정을 마치고 Tinybird 설정하기로 넘어간다.

로그인 오류가 발생한다면 config.production.json에서 staffDeviceVerificationfalse로 수정한다.


Tinybird 설정하기

Tinybird 계정을 생성하고 Workspace에 접속한다.

안내에 따라 workspace를 생성한다.

컨테이너 이름을 수정했다면 tinybird-XXX 부분을 수정한다.

docker compose run --rm tinybird-login를 실행한다.

docker compose run --rm tinybird-sync를 실행한다.

docker compose run --rm tinybird-deploy를 실행한다.

Tinybird files synced into shared volume.가 출력되야 한다.

docker compose run --rm tinybird-login get-tokens를 실행한다.

아래의 값들이 출력된다.

TINYBIRD_API_URL=
TINYBIRD_WORKSPACE_ID=
TINYBIRD_ADMIN_TOKEN=
TINYBIRD_TRACKER_TOKEN=

비어있는 값은 Tinybird Workspace로 접속해서 복사한다.

Tinybird Tokens

TINYBIRD_API_URL=
TINYBIRD_WORKSPACE_ID=
TINYBIRD_ADMIN_TOKEN=
TINYBIRD_TRACKER_TOKEN=

출력된 값으로 .env를 수정한다.

수정 후에는 docker compose up -d를 실행한다.


Ghost Admin 접속하기

https://도메인.com/ghost로 접속하면 Ghost Admin 패널에 접속할 수 있다.

왼쪽 아래 톱니바퀴를 눌러 설정창으로 접속한다.

Analytics 탭으로 가서 Tinybird가 잘 적용되었는지 확인한다.

Network 탭으로 가서 ActivityPub이 잘 적용되었는지 확인한다.


참고 자료

How To Install Ghost With Docker (preview) - Ghost Developer Docs
Preview our new batteries-included tools for self-hosting Ghost using Docker Compose.

How To Install Ghost With Docker (preview)

GitHub - TryGhost/ghost-docker
Contribute to TryGhost/ghost-docker development by creating an account on GitHub.

ghost-docker

ghost-docker/.env.example at main · TryGhost/ghost-docker
Contribute to TryGhost/ghost-docker development by creating an account on GitHub.

ghost-docker/.env.example

ghost-docker/compose.yml at main · TryGhost/ghost-docker
Contribute to TryGhost/ghost-docker development by creating an account on GitHub.

ghost-docker/compose.yml