本番環境にデプロイする
このガイドでは、ローカルで開発してきた Guren アプリケーションを本番環境に載せるまでの手順を、Bun を使った Docker ベースのデプロイを例に説明します。
本番環境にデプロイする
このガイドでは、ローカルで開発してきた Guren アプリケーションを本番環境に載せるまでの手順を、Bun を使った Docker ベースのデプロイを例に説明します。
デプロイ前チェックリスト
デプロイする前に次のコマンドを実行し、問題を早めに見つけておきます。
# フロントエンドとバックエンドをビルド
bun run build
# プロジェクト全体の型チェック
bun run typecheck
# テストスイートを実行
bun run test
# ルート・コントローラー・ページの整合性を検証
bunx guren doctor
エラーが出たら、すべて直してから先に進んでください。doctor コマンドはルート、コントローラー、ページの食い違いを見つけてくれるので、実行時エラーを事前に防げます。
1. 環境変数を設定する
本番環境では、少なくとも次の変数が必要です。
| 変数 | 例 | 用途 |
|---|---|---|
APP_URL |
https://example.com |
公開 URL |
PORT |
3333 |
サーバーが待ち受けるポート |
DATABASE_URL |
postgres://user:pass@host:5432/db |
Postgres の接続文字列 |
APP_KEY |
base64:... |
セッション Cookie とトークン(CSRF、パスワードリセット)の署名、暗号化に使う鍵 |
NODE_ENV=production は次の手順で生成する Dockerfile の中で設定されるため、表には入れていません。
rule
シークレットは git にコミットしないでください。プラットフォームのシークレットマネージャーを使うか、デプロイ時に環境変数として渡してください。
本番用の APP_KEY は次のコマンドで生成します。
bunx guren key:generate
サーバーがキーとして受け付けるのは、base64 でエンコードした 32 バイトの値だけです。openssl rand -hex の出力は使えません。キーのローテーションについては暗号化を参照してください。
イメージをローカルで試すときは、これらの変数を .env.production に書いておきます。次の節の docker run はこのファイルを読み込みます。イメージ自体には .env は入りません。
2. Dockerfile を生成する
Dockerfile は CLI で書き出します。
bunx guren deploy --target docker
書き出される Dockerfile は 2 段階のビルドになっています。ビルダー段階ではすべての依存をインストールして bun run build を実行し、本番段階では実行時の依存だけをインストールします。そのうえでサーバーが実行時に読むものだけをコピーし、NODE_ENV=production で bun bin/serve.ts を起動します。何をコピーしているかの正確な一覧は、生成されたファイルを開いて確認してください。
Guren アプリには dist/ の出力も start スクリプトもないので、ほかの Bun プロジェクトの Dockerfile を流用しても起動しません。アプリが 3333 以外のポートで待ち受ける場合は --port を渡してください。
Dockerfile がすでにあるときは、コマンドはそこで止まります。Guren を更新したら --force を付けて再実行してレシピの変更を取り込み、自分で加えていた変更はそのあとで当て直してください。
ローカルでビルドして動かしてみます。
docker build -t my-app .
docker run -p 3333:3333 --env-file .env.production my-app
3. データベースマイグレーションを実行する
雛形から作ったアプリは、データベースに初めて接続したときに未適用のマイグレーションを適用します。そのため、パイプラインで実行しなくてもコンテナは起動時にマイグレーションを済ませます。それでも、新しいイメージを展開する前にパイプラインで実行しておくほうが安全です。マイグレーションが失敗した時点でデプロイを止められますし、起動するコンテナには適用するものが残っていません。
# CI/CD パイプラインまたはデプロイスクリプト内で
bunx guren db:migrate
ただし例外が 2 つあります。Data API のアダプタは、migrateOnStart を指定したときにだけ起動時にマイグレーションします。D1 のマイグレーションは wrangler d1 migrations apply で適用するため、起動時には実行されません。詳しくはマイグレーションが走るタイミングを参照してください。
4. ヘルスチェックを設定する
ロードバランサーやコンテナオーケストレーターがアプリの稼働を確かめられるように、ヘルスチェック用のエンドポイントをルートに追加します。
import { Router } from '@guren/core'
export function registerWebRoutes(router: Router): void {
router.get('/health', (c) => {
return c.json({ status: 'ok' })
})
// ... 他のルート
}
データベース接続まで確かめる詳しいチェックは、ヘルスチェックを参照してください。
5. 本番用 Docker Compose を構成する
サーバー 1 台で運用するなら、docker-compose.production.yml にまとめておくと管理が楽になります。
services:
app:
build: .
ports:
- "3333:3333"
env_file: .env.production
restart: unless-stopped
depends_on:
postgres:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3333/health"]
interval: 30s
timeout: 5s
retries: 3
postgres:
image: postgres:17
volumes:
- pgdata:/var/lib/postgresql/data
environment:
POSTGRES_USER: ${DB_USER}
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_DB: ${DB_NAME}
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${DB_USER}"]
interval: 10s
timeout: 5s
retries: 5
volumes:
pgdata:
次のコマンドでデプロイします。
docker compose -f docker-compose.production.yml up -d
6. デプロイ後の検証
デプロイが終わったら、すべて正常に動いているかを確かめます。
# ヘルスチェックエンドポイントを確認
curl https://example.com/health
# 期待値: {"status":"ok"}
# ページの読み込みを確認
curl -I https://example.com
# 期待値: HTTP/2 200
# エラーログを確認
docker compose -f docker-compose.production.yml logs app --tail 50
本番環境の強化
アプリが稼働し始めたら、次の対策も検討してください。
- リバースプロキシ: Bun の前に Nginx や Caddy を置き、TLS の終端と静的アセットの配信を任せる
- HSTS:
NODE_ENV=productionではStrict-Transport-Security: max-age=31536000が自動で送られます。includeSubDomainsやpreloadを付けたいときはcreateAppのsecurityHeaders: { hsts: { ... } }で指定し、内部で平文の HTTP を配信する場合はhsts: falseで無効にします - プロセス監視: Docker の
restart: unless-stoppedや systemd などのプロセスマネージャーを使う - ロギング: 構造化ログを出し、集約サービスに転送する
- バックアップ:
pg_dumpやマネージドデータベースのサービスで、Postgres の定期バックアップを組む