Ghost 셀프호스팅 구축기 - n8n 경험을 바탕으로 한 두 번째 도전
n8n 셀프호스팅을 성공한 후, 개인 블로그도 직접 운영해보고 싶어졌습니다. n8n 경험이 있어도 이번엔 수월하지 않을 줄 알았는데, 역시나 새로운 문제들이 기다리고 있었습니다.
Ghost 셀프호스팅을 시작하게 된 배경
n8n 성공 후의 자신감(?)
- n8n 셀프호스팅 완료
- 블로그 필요성: 개인 브랜드와 콘텐츠 관리를 위한 독립적인 플랫폼 필요
- 기술적 호기심
- 결과: 생각보다 쉽지 않았음 😅
Ghost를 선택한 이유
Ghost의 장점:
- 깔끔한 마크다운 에디터
- SEO 최적화가 잘 되어 있음
- 개발자 친화적인 구조
- 셀프호스팅 가능
환경 설정 및 목표
기존 인프라 현황
- OS: Windows 11 + Docker Desktop
- 네트워크: Cloudflare Tunnel (n8n에서 이미 사용 중)
- 도메인: imiwork.com (기존 도메인 활용)
구축 목표
- 서브도메인: blog.imiwork.com으로 Ghost 블로그 운영
- 동시 운영: 기존 n8n 서비스와 함께 안정적 운영
- 관리 편의성: 하나의 Docker Compose로 통합 관리
Before: [Cloudflare Tunnel] → [n8n만]
After: [Cloudflare Tunnel] → [n8n + Ghost]
n8n과 Ghost 동시 운영을 위한 인프라 구성도
첫 번째 시도: 기본 설치의 함정
기본적인 접근
n8n처럼 간단할 줄 알고 기본 설정으로 시작했습니다.
services:
ghost:
image: ghost:latest
ports:
- "2368:2368"
첫 번째 벽: 컨테이너 재시작 루프
# 컨테이너 상태 확인
docker ps
# STATUS: Restarting (1) 3 seconds ago
# 로그 확인으로 원인 파악
docker logs ghost-blog
Error: connect ECONNREFUSED 127.0.0.1:3306
문제 발견: Ghost는 MySQL 데이터베이스가 기본 설정으로 되어있는데, 이를 설정하지 않아서 발생한 오류였습니다.
n8n vs Ghost의 차이점:
- n8n: 자체적으로 데이터 저장 (별도 DB 불필요)
- Ghost: 반드시 데이터베이스 필요 (MySQL, SQLite 등)
데이터베이스 설정: SQLite로 해결
개인 블로그에는 SQLite가 적합
MySQL 서버를 별도로 구축하기보다는, 개인 블로그 용도로는 SQLite가 충분하다고 판단했습니다.
environment:
database__client: sqlite3
database__connection__filename: /var/lib/ghost/content/data/ghost.db
database__useNullAsDefault: 'true'
결과: 컨테이너가 정상 실행되기 시작했습니다! 🎉
SQLite 선택 이유
MySQL vs SQLite 비교:
- MySQL: 고성능, 복잡한 설정, 별도 컨테이너 필요
- SQLite: 설정 간단, 파일 기반, 개인 블로그에 충분한 성능
개인 블로그 특성상 SQLite로 충분하다고 판단
네트워크 접속 문제 해결
localhost 접속 실패
컨테이너는 실행되는데 localhost:2368로 접속할 수 없는 문제가 발생했습니다.
원인 분석: IPv6 바인딩 문제
컨테이너 내부 설정을 확인해보니:
{
"server": {
"host": "::" // IPv6만 바인딩되어 있음
}
}
해결: 설정 파일 직접 수정
{
"url": "https://blog.imiwork.com",
"server": {
"host": "0.0.0.0" // 모든 인터페이스에서 접근 가능하게 변경
}
}
Claude의 도움: "Ghost는 production 모드에서 엄격한 설정을 요구해서 host 설정을 명시적으로 해줘야 해요."
최대 난관: 메일 설정
Ghost의 엄격한 요구사항
Ghost 관리자 계정을 생성하려면 이메일 인증이 필수입니다. 이 부분에서 가장 오랜 시간을 소모했습니다.
시도한 방법들
1. 메일 비활성화 시도
나: "메일 설정 없이 관리자 계정만 만들 수 없나요?"
Claude: "Ghost는 보안상 이메일 인증을 우회할 수 없어요."
결과: 불가능
2. 가짜 메일 서버 시도
mail__transport: stub
결과: 여전히 실패
3. Gmail SMTP 연동 (최종 해결)
실제 이메일 서비스와 연동하는 것이 유일한 해결책이었습니다.
Gmail SMTP 설정 과정
Gmail 앱 비밀번호 생성
- Google 계정 보안 설정 페이지 접속
- 2단계 인증 활성화 (필수 조건)
- 앱 비밀번호 생성
- 16자리 코드 생성 및 복사
Docker Compose 설정 추가
environment:
mail__transport: SMTP
mail__options__service: Gmail
mail__options__auth__user: [email protected]
mail__options__auth__pass: [16자리 앱 비밀번호]
mail__from: [email protected]
주의사항: 일반 Gmail 비밀번호가 아닌 앱 전용 비밀번호를 사용해야 합니다.
Cloudflare Tunnel 확장
기존 n8n 터널에 Ghost 추가
기존 n8n 터널 설정을 확장해서 Ghost도 함께 관리하기로 했습니다.
# ~/.cloudflared/config.yml
tunnel: n8n-tunnel
ingress:
- hostname: n8n.imiwork.com
service: http://localhost:5678
- hostname: blog.imiwork.com # Ghost 추가
service: http://localhost:2368
- service: http_status:404

DNS 레코드 추가
Cloudflare 대시보드에서 새로운 DNS 레코드를 추가해야 했습니다:
Type: CNAME
Name: blog
Target: [Cloudflare 터널 주소]
Claude의 조언: "하나의 터널로 여러 서비스를 관리할 수 있어서 효율적이에요."
최종 통합 설정
완성된 docker-compose.yml
version: '3.8'
services:
n8n:
image: n8nio/n8n:latest
container_name: n8n-docker
restart: always
ports:
- "127.0.0.1:5678:5678"
volumes:
- n8n_data:/home/node/.n8n
environment:
- GENERIC_TIMEZONE=Asia/Seoul
ghost:
image: ghost:latest
container_name: ghost-blog
restart: always
ports:
- "127.0.0.1:2368:2368"
volumes:
- ghost_content:/var/lib/ghost/content
environment:
url: https://blog.imiwork.com
database__client: sqlite3
database__connection__filename: /var/lib/ghost/content/data/ghost.db
database__useNullAsDefault: 'true'
TZ: Asia/Seoul
mail__transport: SMTP
mail__options__service: Gmail
mail__options__auth__user: [email protected]
mail__options__auth__pass: [앱 비밀번호]
mail__from: [email protected]
volumes:
n8n_data:
ghost_content:
n8n vs Ghost: 셀프호스팅 비교
설치 난이도 비교
n8n (첫 번째 경험):
- 기술적 어려움: Docker, CLI 모든 게 낯섦
- 설정 복잡도: 상대적으로 단순 (메일 설정 불필요)
- 심리적 부담: 모든 게 처음이라 높음
Ghost (두 번째 경험):
- 기술적 어려움: Docker, CLI 어느 정도 익숙
- 설정 복잡도: 메일 설정 등 더 복잡한 요구사항
- 심리적 부담: 경험이 있어서 상대적으로 낮음
실제 느낀 점
예상: "Docker 알고 있으니까 Ghost는 쉬울 거야"
현실: "각 서비스마다 고유한 복잡성이 있구나"
교훈: 기술 스택은 비슷해도 각 애플리케이션마다
고유한 요구사항과 함정이 있다
운영 관점에서의 차이
안정성:
- n8n: 워크플로우만 잘 설정하면 거의 손댈 일 없음
- Ghost: 메일 서버 상태에 의존적 (Gmail 계정 문제 시 영향)
확장성:
- n8n: 워크플로우 추가만 하면 됨
- Ghost: 플러그인, 테마 등 확장 요소 많음
셀프호스팅 초보자를 위한 교훈
💡 각 서비스마다 고유한 복잡성이 있다
Docker 경험이 있어도 새로운 서비스는 새로운 도전입니다. 과신하지 말고 차근차근 접근하세요.
💡 공식 문서를 꼼꼼히 읽어보기
Ghost 공식 문서에 메일 설정 필수라고 명시되어 있었는데, 처음에 대충 넘어갔다가 시간을 많이 허비했습니다.
💡 AI 어시스턴트 적극 활용
- "Ghost에서 SQLite 사용하는 방법"
- "Gmail SMTP 설정 방법"
- "Docker 컨테이너가 재시작하는 이유"
💡 통합 관리의 장점
docker-compose로 여러 서비스를 통합 관리하니 훨씬 편리했습니다.

마무리: 성공적인 두 번째 도전
"첫 번째가 어렵다고 두 번째도 쉬운 건 아니지만, 경험이 있으면 문제 해결 과정이 체계적이 된다"
Ghost 셀프호스팅의 핵심은 메일 설정입니다. 이 부분만 제대로 해결하면 나머지는 크게 어렵지 않습니다.
SQLite를 사용하면 데이터베이스 관리 부담도 줄일 수 있고, Cloudflare Tunnel로 HTTPS와 CDN 혜택까지 받을 수 있어서 개인 블로그 용도로는 매우 만족스러운 결과입니다.