【保存版】DjangoとGhostをVPS1台で同居させる「最適解」なDocker構成
はじめに
個人開発やスタートアップの初期フェーズにおいて、「自作のWebアプリ(Django)」と「オウンドメディア(Ghost)」を低コストで運用したいというケースはよくあります。
今回は、ConoHa VPS上にDocker環境を構築し、HTTPS-Portalという強力なリバースプロキシコンテナを使って、以下の要件を満たすデプロイ環境を構築した備忘録です。
実現した構成とメリット
構成図
- VPS: ConoHa VPS
- Frontend proxy: HTTPS-Portal (Nginx + Let's Encrypt auto-renewal)
- App A: Django + PostgreSQL (Webアプリ本体)
- App B: Ghost + MySQL (CMS/ブログ)
この構成のメリット
- SSL証明書の自動更新: HTTPS-PortalがLet's Encryptの取得・更新を全自動で行う。
- マルチドメイン対応: 環境変数を1行書くだけで、ドメインごとの振り分けが可能。
- 静的ファイルの高速配信: DjangoのstaticファイルをNginx(HTTPS-Portal)側から直接配信し、アプリの負荷を下げる。
docker-compose.yml の全体像
以下が今回作成した定義ファイルです。
services:
# --- Django App Section ---
db:
image: postgres:15.2
container_name: postgres
volumes:
- db_data:/var/lib/postgresql/data
healthcheck:
test: pg_isready -U "${POSTGRES_USER:-postgres}" || exit 1
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
env_file:
- .env
app:
container_name: app
build:
context: .
dockerfile: Dockerfile
volumes:
- ./static:/app/static
- ./media:/app/media
expose:
- "8000"
command: sh -c "python manage.py migrate && gunicorn mySite.wsgi:application --bind 0.0.0.0:8000"
env_file:
- .env
depends_on:
db:
condition: service_healthy
# --- Ghost CMS Section ---
ghost-db:
image: mysql:8.0
container_name: ghost-db
restart: always
environment:
MYSQL_ROOT_PASSWORD: ${GHOST_DB_ROOT_PASSWORD}
MYSQL_DATABASE: ghost
MYSQL_USER: ghost
MYSQL_PASSWORD: ${GHOST_DB_PASSWORD}
volumes:
- ghost_db_data:/var/lib/mysql
ghost:
image: ghost:6.10.2
container_name: ghost
restart: always
depends_on:
- ghost-db
ports:
- "2368:2368"
environment:
database__client: mysql
database__connection__host: ghost-db
database__connection__user: ghost
database__connection__password: ${GHOST_DB_PASSWORD}
database__connection__database: ghost
url: sampleA_Ghost.com # 実際の運用ドメインに変更
NODE_ENV: production
volumes:
- ghost_content:/var/lib/ghost/content
# --- Proxy / SSL Section ---
https-portal:
image: steveltn/https-portal:1
container_name: https-portal
ports:
- "80:80"
- "443:443"
environment:
# ここでドメインと転送先コンテナを定義
DOMAINS: 'sampleA_Ghost.com -> http://ghost:2368, sampleB_Django.com -> http://app:8000'
STAGE: 'production' # 本番時はproduction, テスト時はstaging
volumes:
# Djangoの静的ファイルをNginxから配信するためのマウント
- ./static:/var/www/vhosts/sampleB_Django.com/static
- ./media:/var/www/vhosts/sampleB_Django.com/media
- https-portal-data:/var/lib/https-portal
depends_on:
- app
- ghost
volumes:
db_data:
https-portal-data:
ghost_content:
ghost_db_data:
設定のポイント解説
この構成を安定稼働させるための重要なポイントを3つ解説します。
1. HTTPS-Portalによるルーティング設定
https-portal コンテナの DOMAINS 環境変数がルーティングの肝です。
DOMAINS: 'your-media-site.com -> http://ghost:2368, your-app-site.com -> http://app:8000'
このようにカンマ区切りで記述するだけで、Nginxの設定ファイルを編集することなく、ドメイン単位のリバースプロキシ設定とSSL証明書の取得が完了します。
2. Djangoの静的ファイル配信(重要)
Djangoを本番環境(Gunicorn)で動かす際、CSSや画像などの静的ファイルはDjango自体では配信しません。通常はNginx等に任せる必要があります。
今回の構成では、Docker Volumeの共有を利用してこれを解決しています。
- Djangoコンテナ: ./static に collectstatic でファイルを集める。
- HTTPS-Portalコンテナ: 同じ ./static を /var/www/vhosts/ドメイン名/static にマウントする。
HTTPS-Portal(内部のNginx)は、デフォルトで /var/www/vhosts/ドメイン名/ 以下にあるファイルを優先して配信する仕様になっているため、これによりDjangoを経由せずNginxが高速に静的ファイルをレスポンスしてくれます。
静的ファイルの反映
コンテナを起動した直後は、Djangoの静的ファイル(CSSやJavaScript)がボリューム上に存在しないため、管理画面などのレイアウトが崩れた状態になります。
VPS上のターミナルで以下のコマンドを実行し、静的ファイルを一箇所に集めます。
docker-compose exec app python manage.py collectstatic --noinput
3. GhostのURL設定
Ghost側でも url 環境変数を正しく設定する必要があります。これが実際の公開URLと食い違っていると、記事内のリンクや画像パスが壊れる原因になります。
environment:
url: sampleA_Ghost.com
デプロイ手順
- 環境変数の準備 (.env)
セキュリティのため、パスワード類は .env ファイルに記述します。
POSTGRES_USER=myuser
POSTGRES_PASSWORD=mypassword
POSTGRES_DB=mydb
GHOST_DB_ROOT_PASSWORD=rootpass
GHOST_DB_PASSWORD=ghostpass
- Djangoの設定調整
Django側で静的ファイルの出力先や、信頼するプロキシの設定を行います。
# settings.py
STATIC_ROOT = BASE_DIR / 'static'
MEDIA_ROOT = BASE_DIR / 'media'
# HTTPS-Portalからのヘッダーを信頼する設定
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
ALLOWED_HOSTS = ['your-app-site.com']
- コンテナの起動と初期化
# ビルドと起動
docker-compose up -d --build
# Djangoの静的ファイルを集める
docker-compose exec app python manage.py collectstatic --noinput
これで、数分待てばLet's Encryptの証明書が発行され、両方のサイトがHTTPSで閲覧可能になります。
まとめ
VPS 1台の中に、Python製のWebアプリとNode.js製のCMSを同居させる構成は、初期コストを抑える上で非常に有効です。https-portal を活用することで、複雑になりがちなNginxの設定や証明書管理から解放され、開発そのものに集中できるようになります。
Discussion