Docker上でNginx Proxy Managerを構築してHomeLabのルーティングとSSL化を実現する方法

HomeLab tutorial - IT technology blog
HomeLab tutorial - IT technology blog

セルフホスト環境におけるリバースプロキシのアプローチ

ローカルサーバー上でダッシュボード、メディアサーバー、自動化ツールなど複数のWebインターフェースを稼働させ始めると、:8096や:8123、:9000といったポート番号を把握し続けるのはすぐに限界が来ます。これらのサービスを適切な暗号化を施して安全に公開するには、ネットワークのエントリポイントにリバースプロキシレイヤーを配置する必要があります。

さまざまな環境を長年運用してきた中で、HomeLab管理者が採用する手法は主に次の3つに分かれます。

  • 手動によるWebサーバー構成(ベアメタルNginx / Apache): テキスト設定ファイル内でサーバーブロックやバーチャルホストを手書きし、スタンドアロンのcertbotとcronジョブを組み合わせて運用する。
  • エンタープライズ向けエッジプロキシ(Traefik / Caddy): Dockerラベルによる動的なコンテナ検出機能や、最小限の記述で済む自動HTTPS設定ファイルを活用する。
  • GUI管理型リバースプロキシ(Nginx Proxy Manager): NginxとSQLite/MariaDBをベースにしたパッケージ済みWebインターフェースを利用し、Let’s Encryptの検証、プロキシホスト、アクセス制限リストを視覚的に管理する。

各アプローチの比較:メリットとデメリット

1. 手動によるNginx構成

/etc/nginx/sites-available/を直接管理すれば、バッファサイズ、カスタムヘッダーの付与、アップストリームのkeepalive設定など、あらゆるパラメータをきめ細かく制御できます。しかし、数十個もの設定ブロックを手作業で保守するのはヒューマンエラーの原因になります。また、スプリットDNSを用いた複数の内部サブドメインで証明書を更新する際、certbotのフック処理の失敗によって設定が破損してしまうケースも少なくありません。

2. Traefikとラベル駆動型プロキシ

TraefikはDockerソケットのポーリングを通じて、新しいコンテナが起動した瞬間に自動で検出します。手動でホストを作成する手間は省けますが、ラベルのタイプミスやメジャーバージョンアップに伴う構文変更のデバッグには、長時間のログ解析を強いられることがあります。さらに、サービスの半数がメインのDockerデーモン外(専用VMやRaspberry Piなど)で稼働しているようなHomeLabでは、ラベル検出のメリットが薄れてしまいます。

3. Nginx Proxy Manager (NPM)

Nginx Proxy Managerは実用的なバランスを実現しています。バックエンドには標準的なNginxの堅牢性を備えつつ、洗練された管理画面からワイルドカードSSL証明書の発行、アクセスリストの作成、WebSocketの有効化などをワンクリックで設定可能です。実環境での運用経験から言っても、システム管理の負担に追われることなくセルフホスト環境の可用性を維持したいなら、真っ先に習得すべき構成の一つです。

推奨するHomeLabアーキテクチャ

単一障害点によるルーティング障害やコンテナ間の依存関係トラブルを防ぐため、以下のアーキテクチャ構成を推奨します。

  • 分離されたブリッジネットワーク: 外部Dockerネットワーク(例:proxy-network)を作成します。公開したいコンテナをこのネットワークに参加させることで、変動するホストIPアドレスではなく、コンテナ名でターゲットを参照できるようになります。
  • データベースの分離: NPMは組み込みのSQLiteでも動作しますが、MariaDBと連携させることで、ホストの予期せぬ停電時におけるデータベースロックやステートファイルの破損を防ぐことができます。
  • DNS認証によるワイルドカードSSL: HTTP-01 ACME認証のためにポート80をインターネットに開放する代わりに、DNSプロバイダー(Cloudflareなど)と連携したDNS-01認証を設定します。これにより、内部管理ポートを外部に晒すことなく、LAN内だけで有効なLet’s Encryptのワイルドカード証明書(*.yourdomain.com)を取得できます。

構築ガイド:Nginx Proxy Managerのデプロイ

ステップ1:専用Dockerネットワークの準備

スタックを起動する前に、外部ネットワークを作成します。これにより、すべてのサービスを1つの巨大な設定ファイルにまとめることなく、個別のDocker Composeスタックをすっきりとプロキシに接続できます。

docker network create proxy-network

ステップ2:Docker Compose設定の作成

ホスト上にディレクトリ(例:~/homelab/npm)を作成し、デプロイ用ファイルを定義します。将来的にPodman Quadletによるコンテナ管理へ移行する場合でも、 Compose設定を整理しておくことが役立ちます。

mkdir -p ~/homelab/npm && cd ~/homelab/npm
nano docker-compose.yml

本番環境で検証済みの以下のCompose設定を貼り付けます。

version: "3.8"

services:
  app:
    image: "jc21/nginx-proxy-manager:latest"
    container_name: "nginx-proxy-manager"
    restart: unless-stopped
    ports:
      - "80:80"      # パブリックHTTPトラフィック
      - "81:81"      # NPM管理用Web UI
      - "443:443"    # パブリックHTTPSトラフィック
    environment:
      DB_MYSQL_HOST: "npm-db"
      DB_MYSQL_PORT: 3306
      DB_MYSQL_USER: "npm_user"
      DB_MYSQL_PASSWORD: "StrongDbSecretPasswordHere"
      DB_MYSQL_NAME: "npm_database"
    volumes:
      - ./data:/data
      - ./letsencrypt:/etc/letsencrypt
    networks:
      - proxy-network
      - npm-internal
    depends_on:
      - db

  db:
    image: "jc21/mariadb-aria:latest"
    container_name: "npm-db"
    restart: unless-stopped
    environment:
      MYSQL_ROOT_PASSWORD: "SuperRootSecretPasswordHere"
      MYSQL_DATABASE: "npm_database"
      MYSQL_USER: "npm_user"
      MYSQL_PASSWORD: "StrongDbSecretPasswordHere"
    volumes:
      - ./mysql:/var/lib/mysql
    networks:
      - npm-internal

networks:
  proxy-network:
    external: true
  npm-internal:
    driver: bridge

バックグラウンドでコンテナを起動します。

docker compose up -d

ステップ3:初期設定とセキュリティ強化

両方のコンテナが正常に起動したら、ブラウザからホストのローカルIPのポート81(例:http://192.168.1.100:81)にアクセスします。公開エンドポイントを外部の攻撃から守るため、CrowdSecで不正アクセスを検知・防御する構成もあわせて検討するとよいでしょう。

以下のデフォルト認証情報でログインします。

ログイン直後に管理者名義、メールアドレス、パスワードの変更を求められます。まずはこの設定を完了してください。

ステップ4:DNS-01認証によるSSL証明書の自動発行

ポート80のファイアウォール設定ミスで更新が失敗するリスクを避けるため、DNS検証を利用してワイルドカード証明書を取得します。LAN内の名前解決にはAdGuard HomeとUnboundによるローカルDNS環境を活用すると、内部ドメイン宛ての通信をスムーズにルーティングできます。

  1. SSL Certificates > Add SSL Certificate > Let’s Encrypt に移動します。
  2. Domain Names に、ベースドメインとワイルドカードエントリの両方(example.com および *.example.com)を入力します。
  3. Use a DNS Challenge を有効(ON)にします。
  4. DNSプロバイダー(例:Cloudflare)を選択し、必要な権限を絞り込んだDNS APIトークンを認証情報欄に貼り付けます。
dns_cloudflare_api_token = ここに権限を絞り込んだAPIトークンを入力
  1. Let’s Encryptの利用規約に同意し、Save をクリックします。NPMが証明書をリクエストし、一時的なTXTレコードで所有権を検証した上で、バックグラウンドでの自動更新スケジュールを設定します。

ステップ5:最初のプロキシホストの追加

続いて、サーバー上で動作しているサンプルアプリケーションを接続します。ポート3001でリッスンしており、proxy-networkに参加しているuptime-kumaという名前のコンテナがある場合は、以下の手順に従います。

  1. Hosts > Proxy Hosts > Add Proxy Host に移動します。
  2. Details タブの各項目を入力します。
    • Domain Names: status.example.com
    • Scheme: http
    • Forward Hostname / IP: uptime-kuma(Docker内蔵のDNSがコンテナ名を自動解決します)
    • Forward Port: 3001
    • Block Common Exploits および Websockets Support(リアルタイムソケット通信を行うアプリには必須)を有効にします。
  3. SSL タブに切り替えます。
    • ドロップダウンから先ほど取得したワイルドカード証明書を選択します。
    • Force SSL と HTTP/2 Support にチェックを入れます。
    • プロトコルのダウングレード攻撃を防ぐため、HSTS Enabled にチェックを入れます。
  4. Save をクリックします。

現場で役立つ実践Tips

  • ポート81をインターネットに公開しない: ルーターでポート81のポートフォワーディングを設定してはいけません。管理インターフェースへのアクセスは、ローカルLAN内、またはWireGuard/TailscaleなどのプライベートメッシュVPN経由のみに制限してください。
  • Nginxのカスタムチューニング: 大容量ファイルのアップロードが必要なサービスを運用する場合は、NPMの各ホスト設定にある Advanced タブを開き、以下を追加してください。
client_max_body_size 512M;
proxy_read_timeout 600s;
proxy_send_timeout 600s;

この環境を整えておけば、FileBrowserなどのWebファイルマネージャーをはじめとする新しいサービスをHomeLabに追加する際も、30秒足らずで認証付きのSSL暗号化ドメインを割り当てられるようになります。Webサーバーの設定と格闘する時間を減らし、本来のプロジェクト構築に集中しましょう。

Share: