Next.js: Docker セルフホスティング & デプロイメント
最終更新:2026-08-26
セルフホストデプロイメントは、アプリケーションの実行環境を完全に制御できます — コンプライアンス、コスト、またはネットワーク要件によりクラウドプラットフォームを使用できない場合、Docker は最も信頼できるパートナーです。
1. 学習内容
- セルフホスティングの主要なユースケースを理解する:データコンプライアンス / コスト管理 / オンプレミスデプロイ
output: 'standalone'スタンドアロンデプロイモードをnext.config.jsに設定する- マルチステージ Dockerfile(依存関係 → ビルド → 実行)を記述して最小イメージをビルドする
- Nginx をリバースプロキシおよび静的コンテンツサーバーとして使用する
- PM2 を使用してプロセスのデーモン化と自動再起動を実装する
- Docker Compose を使用して App、Nginx、PostgreSQL の 3 つのコンテナをオーケストレーションする
2. ある DevOps エンジニアの実話
(1) 課題:クライアントがデータを国外に転送できないと要求
Charlie は中東の金融機関にサービスを提供する SaaS 企業で働いています。彼らの TaskFlow 製品はサウジアラビアのローカルデータセンターにデプロイする必要があります — クライアントはすべてのユーザーデータをサウジアラビア国内に物理的に保存することを要求しています。
しかし、Vercel にはサウジアラビアにデータセンターがありません。Charlie が直面する問題:
| 課題 | 影響 |
|---|---|
| データ主権コンプライアンス | サウジアラビアの金融規制要件がデータの海外転送を禁止 |
| ネットワーク遅延 | 欧州サーバーからのアクセス時の遅延 > 200 ms |
| ベンダーロックイン | Vercel 月額請求額 $2,000 以上 |
| 内部ネットワーク要件 | 顧客が企業内部ネットワークへのデプロイを希望 |
(2) セルフホスト Docker ソリューション
Charlie は Docker を使用してポータブルなデプロイメントパッケージを構築しました:
# 一度ビルドすれば、どこでも実行可能
docker build -t taskflow:latest .
docker run -p 3000:3000 \
-e DATABASE_URL="postgresql://..." \
-e AUTH_SECRET="..." \
taskflow:latest
(3) 成果
| 項目 | Vercel | セルフホスト Docker |
|---|---|---|
| データ所在地 | Vercel リージョンのみ | 任意のデータセンター |
| 月間コスト | $2,000+ | $300(サーバー) |
| デプロイ遅延 | グローバル ~100 ms | ローカル < 20 ms |
| ベンダーロックイン | 高い | 低い(移行可能) |
3. output: 'standalone' 設定
Next.js 16 の output: 'standalone' モードは、アプリケーションの実行に必要なすべてのファイルを含むスタンドアロン Node.js サーバーを作成します。
graph TB
A[next.config.js] --> B[output: 'standalone']
B --> C[ビルドプロセス]
C --> D[.next/standalone/ ディレクトリ]
D --> E[server.js — 独立 HTTP サーバー]
D --> F[.next/static — 静的リソース]
D --> G[node_modules — 最小依存関係]
D --> H[package.json — 入力設定]
style A fill:#cce5ff
style D fill:#d4edda
(1) next.config.js の設定
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
output: 'standalone',
// 外部処理が必要な依存関係
serverExternalPackages: ['@prisma/client'],
// 本番環境の最適化
productionBrowserSourceMaps: false,
swcMinify: true,
// 画像最適化を保持
images: {
unoptimized: false
}
}
module.exports = nextConfig
(2) standalone 出力ディレクトリ構造
.next/standalone/
├── server.js # 独立 HTTP サーバー(エントリポイント)
├── package.json # ランタイム依存関係の宣言
├── node_modules/ # ビルド時のみの依存関係
├── .next/
│ ├── server/ # サーバーサイドコード
│ ├── static/ # 静的リソース
│ ├── build-manifest.json
│ └── ...
├── public/ # パブリック静的リソース
└── trace # ビルド追跡情報
▶ サンプル: スタンドアロンビルドの検証
# プロジェクトのビルド
npm run build
# standalone ディレクトリサイズの確認
du -sh .next/standalone/
# 専用サーバーの起動
node .next/standalone/server.js
# 別のターミナルで検証
curl http://localhost:3000
.next/standalone/ 358M # 合計サイズ
.next/standalone/server.js # 入力ファイル(自動生成)
.next/standalone/ 358M
node .next/standalone/server.js
▲ Next.js 16.0.0
- Local: http://localhost:3000
✓ Ready in 1.2s
4. マルチステージ Docker ビルド
マルチステージビルドはイメージを 3 つの段階に分割します:依存関係のインストール → アプリケーションのビルド → 最小ランタイム環境。
# ============================================
# Dockerfile — Next.js 16 マルチステージ構築
# ============================================
# --- フェーズ 1: 依存関係のインストール ---
FROM node:20-alpine AS deps
LABEL stage=deps
RUN apk add --no-cache libc6-compat
WORKDIR /app
COPY package.json package-lock.json pnpm-lock.yaml ./
RUN npm ci --only=production && \
npm cache clean --force
# --- フェーズ 2: ビルド ---
FROM node:20-alpine AS build
LABEL stage=build
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
ENV NEXT_TELEMETRY_DISABLED=1
ENV NODE_ENV=production
RUN npm run build
# --- フェーズ 3: 実行 ---
FROM node:20-alpine AS runner
LABEL stage=runner
RUN addgroup --system --gid 1001 nodejs && \
adduser --system --uid 1001 nextjs
WORKDIR /app
# ビルド成果物のコピー
COPY --from=build --chown=nextjs:nodejs \
/app/.next/standalone ./
COPY --from=build --chown=nextjs:nodejs \
/app/.next/static ./.next/static
COPY --from=build --chown=nextjs:nodejs \
/app/public ./public
# ヘルスチェック
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:3000/api/health || exit 1
USER nextjs
EXPOSE 3000
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
ENV NODE_ENV=production
CMD ["node", "server.js"]
(1) ビルドと実行
# イメージのビルド
docker build -t taskflow:latest .
# イメージサイズの確認
docker images taskflow:latest
# コンテナの実行
docker run -d \
--name taskflow-app \
-p 3000:3000 \
-e DATABASE_URL="postgresql://user:pass@host:5432/taskflow" \
-e AUTH_SECRET="your-secret-key" \
-e NEXT_PUBLIC_API_URL="https://api.taskflow.local" \
--restart unless-stopped \
taskflow:latest
(2) ステージごとのイメージサイズ比較
| ステージ | ベースイメージ | サイズ | 内容 |
|---|---|---|---|
| deps | node:20-alpine | ~150 MB | node_modules + システム依存関係 |
| build | node:20-alpine | ~450 MB | ソースコード + node_modules + ビルド成果物 |
| runner | node:20-alpine | ~358 MB | standalone + 本番依存関係 |
| ベース node:20-alpine | — | ~126 MB | 基本システム |
▶ サンプル: Docker での環境変数注入
Sending build context to Docker daemon 4.096kB
Step 1/8 : FROM node:20-alpine AS deps
---> 1dd67de0f936
...
Successfully built a7b3c2d1e0f9
Successfully tagged shophub:latest
Docker command completed successfully.
d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4
# .env ファイルを使用した環境変数の注入
cat > .env.production << EOF
DATABASE_URL=postgresql://user:pass@db:5432/taskflow
AUTH_SECRET=super-secret-key
NEXT_PUBLIC_API_URL=https://api.taskflow.local
NEXT_PUBLIC_POSTHOG_KEY=phc_xxxx
REDIS_URL=redis://redis:6379
EOF
docker run -d \
--name taskflow-app \
--env-file .env.production \
-p 3000:3000 \
--network taskflow-net \
taskflow:latest
Docker operation completed.
5. Nginx リバースプロキシ
Nginx は SSL 終端、静的リソースキャッシュ、ロードバランシングを処理し、本番環境に不可欠なコンポーネントです。
graph LR
A[ユーザーのブラウザ] --> B[Nginx :443]
B --> C{パスマッチング}
C -->|/_next/static/*| D[Nginx 直接配信<br/>1 年間キャッシュ]
C -->|/api/health| E[Next.js :3000]
C -->|/*| E
B --> F[SSL 終端<br/>Let's Encrypt]
style B fill:#cce5ff
style D fill:#d4edda
Nginx 設定
# nginx/nginx.conf
upstream nextjs_upstream {
server app:3000;
keepalive 64;
}
server {
listen 80;
server_name taskflow.local;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name taskflow.local;
# SSL 証明書
ssl_certificate /etc/nginx/ssl/taskflow.crt;
ssl_certificate_key /etc/nginx/ssl/taskflow.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
# セキュリティヘッダー
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
# ログ
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
# 静的リソースのキャッシュ(Next.js で処理)
location /_next/static/ {
proxy_pass http://nextjs_upstream;
expires 365d;
add_header Cache-Control "public, immutable";
}
location /static/ {
proxy_pass http://nextjs_upstream;
expires 30d;
add_header Cache-Control "public";
}
# ヘルスチェックエンドポイント
location /api/health {
proxy_pass http://nextjs_upstream;
access_log off;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
# その他すべてのリクエストは Next.js に転送
location / {
proxy_pass http://nextjs_upstream;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 60s;
proxy_send_timeout 60s;
}
}
6. PM2 プロセスマネージャー
PM2 は Node.js プロセスがクラッシュ後に自動再起動することを保証し、ログ管理とクラスターモードを提供します。
(1) PM2 設定
// ecosystem.config.js
module.exports = {
apps: [{
name: 'taskflow',
script: 'server.js',
cwd: '/app',
// クラスターモード(全 CPU コアを使用)
exec_mode: 'cluster',
instances: 'max',
// 環境変数
env: {
NODE_ENV: 'production',
PORT: 3000,
HOSTNAME: '0.0.0.0'
},
// ログ設定
log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
error_file: '/var/log/pm2/taskflow-error.log',
out_file: '/var/log/pm2/taskflow-out.log',
merge_logs: true,
// 自動再起動
max_restarts: 10,
restart_delay: 1000,
min_uptime: 5000,
// メモリ監視
max_memory_restart: '500M',
// ヘルスチェック
listen_timeout: 3000,
kill_timeout: 5000
}]
}
(2) PM2 Docker 統合
# runner ステージに PM2 をインストール
FROM node:20-alpine AS runner
RUN npm install -g pm2 && \
addgroup --system --gid 1001 nodejs && \
adduser --system --uid 1001 nextjs
WORKDIR /app
COPY --from=build --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=build --chown=nextjs:nodejs /app/.next/static ./.next/static
COPY --from=build --chown=nextjs:nodejs /app/public ./public
COPY --chown=nextjs:nodejs ecosystem.config.js ./
USER nextjs
EXPOSE 3000
# PM2 でクラスターモードを有効化
CMD ["pm2-runtime", "start", "ecosystem.config.js"]
▶ サンプル: よく使う PM2 コマンド
Docker image built and container started successfully.
# 全プロセスの表示
pm2 list
# ログの表示
pm2 logs taskflow
pm2 logs taskflow --lines 100
# リソースの監視
pm2 monit
# リロード(ゼロダウンタイム)
pm2 reload taskflow
# 停止/再起動
pm2 stop taskflow
pm2 restart taskflow
# 現在のプロセスリストを保存
pm2 save
pm2 startup
PM2 command executed successfully.
PM2 command executed successfully.
PM2 command executed successfully.
PM2 command executed successfully.
PM2 command executed successfully.
PM2 command executed successfully.
┌────┬──────────┬─────────┬─────────┐
│ id │ name │ status │ cpu │
├────┼──────────┼─────────┼─────────┤
│ 0 │ shophub │ online │ 0% │
└────┴──────────┴─────────┴─────────┘
PM2 command executed successfully.
┌────┬──────────┬─────────┬─────────┐
│ id │ name │ status │ cpu │
├────┼──────────┼─────────┼─────────┤
│ 0 │ shophub │ online │ 0% │
└────┴──────────┴─────────┴─────────┘
7. Docker Compose: 3 つのコンテナをオーケストレーション
Docker Compose は App、Nginx、PostgreSQL の 3 つのコンテナをオーケストレーションし、ワンクリックで環境全体を起動します。
# docker-compose.yml
version: '3.8'
networks:
taskflow-net:
driver: bridge
volumes:
postgres-data:
driver: local
nginx-logs:
driver: local
services:
# === 1. PostgreSQL データベース ===
db:
image: postgres:16-alpine
container_name: taskflow-db
restart: unless-stopped
networks:
- taskflow-net
volumes:
- postgres-data:/var/lib/postgresql/data
- ./db/init:/docker-entrypoint-initdb.d
environment:
POSTGRES_USER: taskflow
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_DB: taskflow
healthcheck:
test: ["CMD-SHELL", "pg_isready -U taskflow"]
interval: 10s
timeout: 5s
retries: 5
ports:
- "5432:5432"
# === 2. Next.js アプリケーション ===
app:
build:
context: .
dockerfile: Dockerfile
target: runner
image: taskflow:latest
container_name: taskflow-app
restart: unless-stopped
networks:
- taskflow-net
depends_on:
db:
condition: service_healthy
environment:
NODE_ENV: production
PORT: 3000
HOSTNAME: "0.0.0.0"
DATABASE_URL: postgresql://taskflow:${DB_PASSWORD}@db:5432/taskflow
AUTH_SECRET: ${AUTH_SECRET}
AUTH_URL: ${AUTH_URL}
NEXT_PUBLIC_API_URL: ${PUBLIC_API_URL}
NEXT_PUBLIC_POSTHOG_KEY: ${POSTHOG_KEY:-}
env_file:
- .env.production
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3000/api/health"]
interval: 30s
timeout: 3s
retries: 3
# === 3. Nginx リバースプロキシ ===
nginx:
image: nginx:alpine
container_name: taskflow-nginx
restart: unless-stopped
networks:
- taskflow-net
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf:ro
- ./nginx/ssl:/etc/nginx/ssl:ro
- nginx-logs:/var/log/nginx
depends_on:
app:
condition: service_healthy
(1) 環境変数ファイル
# .env.production(Git にコミットしないでください)
DB_PASSWORD=StrongPassword123!
AUTH_SECRET=your-auth-secret-key-min-32-chars
AUTH_URL=https://auth.taskflow.local
PUBLIC_API_URL=https://api.taskflow.local
POSTHOG_KEY=phc_exampleKey123
(2) 起動と管理
# 初回起動
docker compose up -d
# ログの表示
docker compose logs -f app
docker compose logs -f nginx
# アプリケーションの再ビルド
docker compose build app
docker compose up -d app
# データベースマイグレーションの更新
docker compose exec app npx prisma migrate deploy
# 稼働状況の表示
docker compose ps
# 全サービスの停止
docker compose down
# 完全クリーンアップ(ボリューム含む)
docker compose down -v
▶ サンプル: docker-compose.override.yml(開発環境)
上記の YAML 設定を指定されたファイルパスに保存してください。設定は次回のサーバー再起動時に有効になります。
# docker-compose.override.yml
version: '3.8'
services:
app:
build:
target: build # 開発フェーズでは runner ではなく build を使用
environment:
NODE_ENV: development
volumes:
- ./src:/app/src:ro
- ./public:/app/public:ro
command: npm run dev # 開発サーバーを使用
db:
ports:
- "5432:5432" # 開発中はデータベースポートを公開
nginx:
ports:
- "3000:80" # 開発中はポートマッピングを簡略化
上記の YAML 設定を指定されたファイルパスに保存してください。設定は次回のサーバー再起動時に有効になります。
8. ランタイム環境変数の注入
Docker コンテナの環境変数はビルド時ではなく実行時に注入されます — これにより単一のイメージを複数の環境にデプロイできます。
graph LR
A[Docker ビルド] --> B[イメージ<br/>(環境変数なし)]
B --> C[ランタイム注入]
C --> D[開発環境 .env.dev]
C --> E[テスト環境 .env.test]
C --> F[本番環境 .env.prod]
D --> G[コンテナ起動]
E --> G
F --> G
G --> H[server.js が process.env を読み取り]
style B fill:#cce5ff
style G fill:#d4edda
(1) ビルド時変数とランタイム変数
| 変数タイプ | 注入タイミング | 例 | 保存場所 |
|---|---|---|---|
| ビルド時 | docker build |
NEXT_PUBLIC_*、バージョン番号 |
Dockerfile ARG |
| ランタイム | docker run |
DATABASE_URL、AUTH_SECRET |
docker compose env_file |
| 混合 | 両方必要 | NEXT_PUBLIC_API_URL |
ビルド時にフロントエンドに注入、ランタイムにバックエンドに注入 |
(2) ビルド時の変数注入
# Dockerfile で ARG を使用してビルド時変数を渡す
FROM node:20-alpine AS build
ARG NEXT_PUBLIC_API_URL
ENV NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL
ARG SENTRY_DSN
ENV SENTRY_DSN=$SENTRY_DSN
RUN npm run build
# ビルド時に変数を渡す
docker build \
--build-arg NEXT_PUBLIC_API_URL=https://api.taskflow.com \
--build-arg SENTRY_DSN=https://xxx@sentry.io/123 \
-t taskflow:latest .
▶ サンプル: ランタイム環境検証スクリプト
Sending build context to Docker daemon 4.096kB
Step 1/8 : FROM node:20-alpine AS deps
---> 1dd67de0f936
...
Successfully built a7b3c2d1e0f9
Successfully tagged shophub:latest
// src/lib/env.ts
// ランタイム環境変数検証
function getRequiredEnvVar(name: string): string {
const value = process.env[name]
if (!value) {
throw new Error(`必須の環境変数が不足しています: ${name}`)
}
return value
}
export const env = {
databaseUrl: getRequiredEnvVar('DATABASE_URL'),
authSecret: getRequiredEnvVar('AUTH_SECRET'),
authUrl: process.env.AUTH_URL || 'http://localhost:3000',
publicApiUrl: process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000',
posthogKey: process.env.NEXT_PUBLIC_POSTHOG_KEY,
nodeEnv: process.env.NODE_ENV || 'development',
isProduction: process.env.NODE_ENV === 'production',
port: parseInt(process.env.PORT || '3000', 10)
}
エクスポート: env。
9. 完全な例: TaskFlow Docker デプロイメント
# ============================================
# 本番デプロイメントスクリプト: deploy.sh
# 機能: ビルド → マイグレーション → 起動 → ヘルスチェック
# ============================================
#!/bin/bash
set -euo pipefail
echo "=== TaskFlow 本番デプロイメント ==="
# 1. 環境変数の読み込み
if [ ! -f .env.production ]; then
echo "エラー: .env.production が見つかりません"
exit 1
fi
source .env.production
# 2. Docker イメージのビルド
echo "Docker イメージをビルド中..."
docker compose build app
# 3. データベースの起動(実行中でない場合)
echo "データベースを起動中..."
docker compose up -d db
echo "データベースの準備完了を待機中..."
sleep 5
# 4. データベースマイグレーションの実行
echo "データベースマイグレーションを実行中..."
docker compose run --rm app npx prisma migrate deploy
# 5. アプリと Nginx の起動
echo "アプリケーションと Nginx を起動中..."
docker compose up -d app nginx
# 6. ヘルスチェック
echo "ヘルスチェックを実行中..."
for i in {1..10}; do
if curl -s -o /dev/null -w "%{http_code}" http://localhost:80/api/health | grep -q 200; then
echo "ヘルスチェック合格!"
break
fi
echo "待機中... ($i/10)"
sleep 3
done
# 7. 古いイメージのクリーンアップ
echo "古いイメージをクリーンアップ中..."
docker image prune -f
# 8. デプロイメント情報
echo ""
echo "=== デプロイメント完了 ==="
echo "アプリ: http://localhost:80"
echo "API: http://localhost:80/api/health"
echo "DB: postgresql://taskflow@localhost:5432/taskflow"
echo "ログ: docker compose logs -f app"
echo "再起動: docker compose restart app"
// src/app/api/health/route.ts
// ============================================
// ヘルスチェック API: Docker HEALTHCHECK から呼び出されます
// ============================================
import { NextResponse } from 'next/server'
import { prisma } from '@/lib/prisma'
export async function GET() {
const checks = {
status: 'healthy',
timestamp: new Date().toISOString(),
uptime: process.uptime(),
memory: process.memoryUsage(),
checks: {} as Record<string, boolean>
}
try {
// データベース接続の確認
await prisma.$queryRaw`SELECT 1`
checks.checks.database = true
} catch {
checks.checks.database = false
checks.status = 'degraded'
}
try {
// Redis の確認(設定されている場合)
// await redis.ping()
checks.checks.redis = true
} catch {
checks.checks.redis = false
if (!checks.checks.database) {
checks.status = 'unhealthy'
}
}
const statusCode = checks.status === 'healthy' ? 200 : 503
return NextResponse.json(checks, { status: statusCode })
}
❓ よくある質問
output: 'standalone' と output: 'export' の違いは何ですか?standalone は Node.js サーバーを含むスタンドアロンパッケージを生成し、SSR、ISR、API ルートなどのすべての Next.js 機能をサポートします。export は純粋な静的 HTML を生成し(SSR は無効になります)、CDN ホスティングに適しています。Docker でのセルフホスティングには、standalone モードを使用する必要があります。restart: unless-stopped はコンテナレベルのクラッシュ(OOM など)を処理し、PM2 は Node.js プロセスレベルのクラッシュ(未捕捉例外など)を処理します。また、PM2 はログローテーション、クラスターモード、ゼロダウンタイム再起動など、Docker 自体にはない機能も提供します。📖 まとめ
next.config.jsのoutput: 'standalone'設定はセルフホスト Docker の前提条件であり、スタンドアロン Node.js サーバーを生成します。- マルチステージ Docker ビルド(deps → build → runner)はイメージを約 358MB に圧縮し、転送コストとストレージコストを削減します。
- Nginx はリバースプロキシとして機能し、SSL 終端、静的キャッシュ、セキュリティヘッダー注入を提供し、本番環境に不可欠です。
- PM2 はプロセスデーモン、クラスターモード、ゼロダウンタイム再起動、ログ管理を提供し、アプリケーションの信頼性を向上させます。
- Docker Compose は App、Nginx、PostgreSQL の 3 つのコンテナをオーケストレーションし、
docker compose up -dでワンクリック起動します。 - ランタイム環境変数は
docker run -eまたはenv_fileで注入され、単一イメージのマルチ環境デプロイを実現します。
📝 練習問題
-
基本問題 (⭐):
output: 'standalone'を含むnext.config.jsを作成し、マルチステージ Dockerfile を記述して、正常にビルドしてdocker runを実行し、curl localhost:3000で検証します。 -
応用問題 (⭐⭐): Docker Compose に Nginx リバースプロキシコンテナを追加します:(1) 自己署名 SSL 証明書を設定する。(2) 静的リソースのキャッシュルールを追加する。(3)
/_next/staticを 365 日間キャッシュするように設定する。(4) HTTPS アクセスが正常に機能することを確認する。 -
発展問題 (⭐⭐⭐): 完全なセルフホスト CI/CD + Docker パイプラインを構築します:(1) GitHub Actions を使用して Docker イメージを自動ビルドし GHCR にプッシュする。(2) SSH 経由でターゲットサーバーに新しいイメージをプルする。(3) Docker Compose を使用してゼロダウンタイム更新を行う(
docker compose up -d --no-deps --build app)。(4) PM2 クラスターモードとログローテーションを設定する。