WEB工房しずおかのロゴ

WEB工房しずおか

ConoHa VPS×Ubuntu 24.04で挑むWEBサーバー構築記【Laravel+Next.jsモノレポ(5サイト)同居デプロイ&Certbot一括SSL化編】

WEB
Koshio

ConoHa VPS Ubuntu 24.04 同居デプロイ&SSL化

みなさん、こんにちは!WEB工房しずおかの開発チームです。

[前回の記事(ConoHa VPS×Ubuntu 24.04で挑むWEBサーバー構築記【LNMP+Node.js/pnpm環境構築編】)]では、1台のVPS上で「Laravel + ReactオリジナルCMS」と「Next.js 5サイトのモノレポ構成」を動かすためのミドルウェア(Nginx, PHP 8.3, MySQL 8.0, Node.js 20, pnpm)のセットアップを行いました。

連載第3弾(完結編)となる今回は、いよいよ**「アプリケーションの配置・ビルド」「PM2によるプロセス管理」「Nginxのリバースプロキシ設定」「Certbotによる全ドメインの一括SSL(HTTPS)化」**を一気に進めていきます!

実際に遭遇したTypeScriptの型エラーやPM2のモノレポ特有のハマりどころとその解決策まで、リアルなノウハウを凝縮してお届けします。


1. 全体構成とマルチドメイン設計

今回は、単一のConoHa VPS(IP: 160.251.166.124)上に以下の全6ドメイン(+ www サブドメイン)を紐付け、バックエンドとフロントエンドを連携させます。

| ドメイン / FQDN | 役割 / 内部構成 | 内部ポート / ソケット | | :--- | :--- | :--- | | fujisan.kusanagi.city | バックエンド(Laravel 11 + PHP-FPM) | unix:/var/run/php/php8.3-fpm.sock | | kusanagi.city / www.kusanagi.city | フロントエンド①(Next.js / Turborepo - apps/city) | http://127.0.0.1:3001 | | kusanagi.systems | フロントエンド②(Next.js / Turborepo - apps/systems) | http://127.0.0.1:3002 | | kusanagi.design | フロントエンド③(Next.js / Turborepo - apps/design) | http://127.0.0.1:3003 | | kusanagi.life | フロントエンド④(Next.js / Turborepo - apps/life) | http://127.0.0.1:3004 | | kusanagi.institute | フロントエンド⑤(Next.js / Turborepo - apps/institute) | http://127.0.0.1:3005 |


2. アプリケーションの配置とビルドエラーの克服

① 共有パッケージ(packages/core)の参照設定

Turborepoによるモノレポ構成では、共通コンポーネントやAPIクライアントを packages/core に集約しています。apps/city 等の各アプリから参照する際、呼び出しパスが自身のアプリ配下(@/...)を向いていると Module not found となるため、共有パッケージからのインポート形式に修正します。

// ❌ 誤り(自身の apps/city 内を探してしまう)
import api from '@/lib/api';
import LexicalContent from '@/components/LexicalContent';

// ⭕️ 正しい記述(モノレポ内の共有パッケージから読み込む)
import { api, LexicalContent } from '@kusanagi/core';

また、各アプリの package.json"@kusanagi/core": "workspace:*" が指定されていることを確認します。


② ビルド時に発生したTypeScript型エラーの修正

pnpm build(または turbo build)を実行した際、プロダクションビルドの型チェックで発生した2つの大きなエラーの対処法です。

1. Next.js App Router における Metadatacanonical 型エラー

Next.js(App Router)の Metadata 型において、canonical は直下のプロパティではなく alternates の中にネストして定義する仕様になっています。

  • エラーログ: Type error: Object literal may only specify known properties, and 'canonical' does not exist in type 'Metadata'.
// ⭕️ 修正後(apps/city/src/app/articles/[id]/page.tsx)
export async function generateMetadata(): Promise<Metadata> {
  return {
    title: '...',
    description: '...',
    alternates: {
      canonical: pageUrl, // alternatesの中に配置
    },
    openGraph: { ... }
  };
}

2. Framer Motion のアニメーション型推論エラー

イージング関数などの文字列が単なる string 型と扱われ、Framer Motion側の厳格な Easing 型と衝突してビルドが停止する問題です。

  • エラーログ: Type 'string' is not assignable to type 'Easing | Easing[] | undefined'.
// ⭕️ 修正後(HeroSection.tsx)
import { motion, Variants } from 'framer-motion';

// 明示的に : Variants 型をつけることで厳格な型推論を通す
const itemVariants: Variants = {
  hidden: { opacity: 0, y: 20 },
  visible: {
    opacity: 1,
    y: 0,
    transition: { duration: 0.8, ease: "easeOut" }
  }
};

3. PM2によるNext.jsモノレポのプロセス管理

複数サイトを常時起動・自動再起させるため、プロセス管理ツール PM2 を使用します。

ERR_PNPM_OTHER_PM_EXPECTED トラブルの回避

pnpm 経由でコマンドを動かそうとした際、ルートの package.json"packageManager": "npm@..." の記述が残っていると ERR_PNPM_OTHER_PM_EXPECTED でプロセスが即座にクラッシュします。 設定ファイルから packageManager の行を削除するか、pnpm のバージョンを指定し直すことで解決します。

② PM2 起動コマンドの「決定版」

PM2から pnpm --filter ... などのラッパーを経由して起動しようとすると、引数の伝播(-- -p 3001)がうまくいかず Invalid project directoryCommand "filter" not found となって失敗します。

【解決策】
各アプリのルートディレクトリ(--cwd)を明示し、node_modules 内の Next.js 実行バイナリを直接叩く方式が最も確実で動作が安定します。

# PM2をグローバルインストール(npm経由が確実)
$ sudo npm install -g pm2

# 各サイトを個別のポートで登録・起動
$ pm2 start /var/www/kusanagi-monorepo/node_modules/next/dist/bin/next --name "kusanagi-city" --cwd /var/www/kusanagi-monorepo/apps/city -- start -p 3001
$ pm2 start /var/www/kusanagi-monorepo/node_modules/next/dist/bin/next --name "kusanagi-systems" --cwd /var/www/kusanagi-monorepo/apps/systems -- start -p 3002
$ pm2 start /var/www/kusanagi-monorepo/node_modules/next/dist/bin/next --name "kusanagi-design" --cwd /var/www/kusanagi-monorepo/apps/design -- start -p 3003
$ pm2 start /var/www/kusanagi-monorepo/node_modules/next/dist/bin/next --name "kusanagi-life" --cwd /var/www/kusanagi-monorepo/apps/life -- start -p 3004
$ pm2 start /var/www/kusanagi-monorepo/node_modules/next/dist/bin/next --name "kusanagi-institute" --cwd /var/www/kusanagi-monorepo/apps/institute -- start -p 3005

# プロセス一覧の確認(全サイト STATUS: online を確認)
$ pm2 status

# サーバー再起動時にも全自動で復元されるよう保存
$ pm2 startup
$ pm2 save

4. Nginx設定&ファイアウォール(UFW)の解放

① アクセス時の「タイムアウト(応答なし)」を解消する

初期状態ではConoHa VPS上の UFW(ファイアウォール)により 80 / 443 番ポートが閉じられており、ブラウザアクセスが無限読み込みになります。

# HTTP (80) および HTTPS (443) の通信を許可
$ sudo ufw allow 80/tcp
$ sudo ufw allow 443/tcp
$ sudo ufw reload

② Nginx設定ファイル(www サブドメインの併記)

/etc/nginx/sites-available/kusanagi.city を作成し、www.kusanagi.city も同一の Next.js アプリへリバースプロキシされるように記述します。

server {
    listen 80;
    server_name kusanagi.city www.kusanagi.city;

    location / {
        proxy_pass [http://127.0.0.1:3001](http://127.0.0.1:3001);
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

設定ファイルをシンボリックリンク化し、構文チェックを行って反映します。

$ sudo ln -sf /etc/nginx/sites-available/kusanagi.city /etc/nginx/sites-enabled/
$ sudo nginx -t
$ sudo systemctl reload nginx

5. Certbot(Let's Encrypt)による全ドメイン一括 SSL化

全6ドメイン(および www.kusanagi.city を含む全7FQDN)を一括で HTTPS 化します。

# snap経由で最新のCertbotを導入
$ sudo apt remove certbot -y
$ sudo snap install --classic certbot
$ sudo ln -sf /snap/bin/certbot /usr/bin/certbot

# 全7FQDNを一括指定して証明書発行&Nginx設定の自動更新
$ sudo certbot --nginx \
  -d fujisan.kusanagi.city \
  -d kusanagi.city \
  -d www.kusanagi.city \
  -d kusanagi.systems \
  -d kusanagi.design \
  -d kusanagi.life \
  -d kusanagi.institute

実行中の対話画面でメールアドレスの登録や利用規約(Y)に同意すれば、Certbot が Nginx の設定ファイルへ HTTPS(443ポート)設定と http://https:// への自動リダイレクト設定をすべて書き加えてくれます。

# 自動更新機能のシミュレーションテスト
$ sudo certbot renew --dry-run

Congratulations, all simulated renewals succeeded と表示されれば、今後のSSL更新も全自動で行われます!


まとめ

全3回の連載を通じて、ConoHa VPS(Ubuntu 24.04)上への 「Laravelバックエンド + Next.jsモノレポ5サイト」 のマルチドメイン同居環境が無事に完成しました!

  1. インフラ・セキュリティ: 独自SSHポート(8823)運用とUFWによるアクセス制御
  2. ミドルウェア: LNMP(Nginx, PHP 8.3-FPM, MySQL 8.0)+ Node.js 20/pnpm の構築
  3. フロントエンド運用: Turborepo のプロダクションビルド&PM2 によるマルチポート並行稼働
  4. Webサーバー・SSL: Nginx リバースプロキシ&Certbot による全ドメイン一括 SSL化

WEB工房しずおかでは、単一サイトの構築にとどまらず、Laravel×Next.jsのような高度でモダンな構成、モノレポ構成のインフラ設計・パフォーマンス最適化を幅広く承っております。大規模な構成変更やVPS構築でお困りの際は、ぜひお気軽にお問い合わせください!