[Self-Hosted] 개인용 블로그로 Ghost를 설치해보자
나는 기록용 블로그로 Ghost를 사용 중이다.
Ghost에는 회원, 멤버십, 뉴스레터 등 다양한 기능들이 있다.
자세한 기능은 아래에서 확인할 수 있다.
Ghost Documentation
설치하기
Ghost는 도커 이미지와 구축 가이드를 공식적으로 지원한다.
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/mysqlhttps://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,activitypubGhost에는 트래픽 분석 서비스(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=ghostpasswordPASSWORD는 openssl rand -hex 32 명령어로 각각 생성하고 수정한다.
DATABASE_USER는 지정하지 않을 시 기본값은 ghost이다.
# ActivityPub
# If you'd prefer to self-host ActivityPub yourself uncomment the line below
# ACTIVITYPUB_TARGET=activitypub:8080Caddy 컨테이너에서 사용할 환경 변수이다.
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=1234567890mail__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-alpineGhost 도커 이미지 태그를 지정할 수 있다.
# 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=2368Ghost 도커 컨테이너 호스트 포트이다.
# Data locations
# Location to store uploaded data
UPLOAD_LOCATION=./data/ghost
# Location for database data
MYSQL_DATA_LOCATION=./data/mysqlGhost와 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 설정이 필요하다.
여기서는 다루지 않는다.
Configuring with Mailgun
더 많은 설정은 아래에서 확인할 수 있다.
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-login과 tinybird-deploy는 build:가 필요하므로 처음에 복제한 저장소 안에 있는 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에서 staffDeviceVerification를 false로 수정한다.
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_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-docker
ghost-docker/.env.example
ghost-docker/compose.yml




