Docker ComposeでNginx Proxy Managerを構築する:WebUIで管理するリバースプロキシと自動SSL

Docker tutorial - IT technology blog
Docker tutorial - IT technology blog

実際の課題:1台のVPSで複数サービスを管理する

同じVPS上でNextcloud、Gitea、APIバックエンド、WordPressなど複数のアプリを動かしていると、すぐに古典的な問題にぶつかります。すべてのサービスがポート80と443で待ち受けたいのに、各ポートでリッスンできるプロセスは1つだけという制約です。

従来の解決策はNginxを手動でリバースプロキシとして設定することでした。新しいサービスを追加するたびに、設定ファイルを作成し、Let’s EncryptからSSL証明書を取得し、Nginxをリロードする必要があります。技術的に難しいわけではありませんが、繰り返し作業でミスが起きやすく、特に5〜10個のサブドメインを同時に管理するときは負担になります。

3つの主要アプローチ — 率直な比較

1. 素のNginx + 手動Certbot

メリット:軽量で完全なコントロールが可能、追加ツールへの依存なし。

デメリット:ドメインを追加するたびにcertbotを実行し、/etc/nginx/sites-available/を編集してnginx -t && systemctl reload nginxを実行する必要があります。10個のサブドメインになると、これが保守の重荷になります。

2. Traefik

メリット:TraefikはDockerとの統合が優れており、ラベルによるコンテナの自動検出やLet’s Encryptのサポートが充実しています。

デメリット:YAMLファイルとラベルによる設定が複雑で学習曲線が急です。ルーティング状態を確認できる直感的なWeb UIがありません。

3. Nginx Proxy Manager(NPM)

メリット:美しいWeb UI、フォーム入力だけでプロキシホストを追加可能、ワンクリックでLet’s Encrypt SSLを自動取得、アクセスリスト・リダイレクト・ストリームプロキシにも対応。

デメリット:データベース(SQLiteまたはMariaDB)が付随するためやや重く、正しく使うにはリバースプロキシの基本的な理解が必要です。

Nginx Proxy Managerを選ぶべきタイミング

NPMが最適なのは次のようなケースです:

  • 個人またはスモールチームのVPSを管理していて、リソース最適化よりデプロイ速度を優先したい
  • チームにNginxの設定ファイルを手動で編集するのが得意でないメンバーがいる
  • 頻繁にサブドメインの追加・削除が必要で、そのたびにSSHでサーバーに入りたくない
  • すべてのプロキシホストの状態をダッシュボードで一目確認したい

Docker Composeを使った初めての実プロジェクトでは、今思えば笑えるような基本的なミスをたくさん犯しました。その中でも特に印象的だったのは、コンテナが独自のネットワーク内で動いているのに、Docker外部でNginxを手動設定しようとしたことです。NPMがDockerネットワーク内で動作することを知ってから、その問題がすっきり解決しました。

Docker ComposeでNginx Proxy Managerをデプロイする

事前要件

  • LinuxのVPS(Ubuntu 22.04またはDebian 12推奨)
  • DockerとDocker Composeがインストール済み
  • ドメインのAレコードがVPSのIPに向いていること
  • ファイアウォールでポート80、443、81が開放されていること

ステップ1:ディレクトリとdocker-compose.ymlの作成

mkdir -p ~/npm && cd ~/npm

docker-compose.ymlファイルを作成します:

version: '3.8'

services:
  npm:
    image: 'jc21/nginx-proxy-manager:latest'
    container_name: nginx-proxy-manager
    restart: unless-stopped
    ports:
      - '80:80'    # HTTP
      - '443:443'  # HTTPS
      - '81:81'    # Web UI
    volumes:
      - ./data:/data
      - ./letsencrypt:/etc/letsencrypt

networks:
  default:
    name: npm_network
    driver: bridge

この構成はデフォルトでSQLiteを使用します。個人VPSには十分です。スケールアップやバックアップを容易にしたい場合は、スタックにMariaDBを追加することもできます。

ステップ2:NPMを起動する

docker compose up -d

# ログを確認する
docker compose logs -f npm

約30秒後、http://<VPSのIP>:81にアクセスして管理画面を開きます。

デフォルトのログイン情報:

初回ログイン後すぐに変更してください。

ステップ3:最初のプロキシホストを追加する

Dockerネットワークnpm_network内でポート3000で待ち受けているmyappというコンテナがあるとします。

  1. Proxy HostsAdd Proxy Hostを開く
  2. Domain Names:app.yourdomain.comを入力
  3. Scheme:http
  4. Forward Hostname/IP:コンテナ名(myapp)または内部IP
  5. Forward Port:3000
  6. 必要に応じてBlock Common ExploitsWebsockets Supportを有効化
  7. SSLタブ → Request a new SSL Certificateを選択 → Force SSLHTTP/2 Supportを有効化
  8. Let’s Encryptのメールアドレスを入力 → 同意にチェック → Save

NPMが自動的にLet’s Encryptから証明書を取得し、HTTPSを設定します。一連のプロセスは30秒以内に完了します。

ステップ4:Dockerアプリケーションを同じネットワークに接続する

NPMが他のコンテナにリクエストをフォワードできるよう、同じDockerネットワークに参加させる必要があります。最も簡単な方法は、アプリケーションのdocker-compose.ymlでexternalネットワークを宣言することです:

services:
  myapp:
    image: myapp:latest
    container_name: myapp
    # ホストへのポート公開は不要
    networks:
      - npm_network

networks:
  npm_network:
    external: true

この方法では、myappはホストにポートを公開する必要がありません。インターネットからのトラフィックはNPMだけが受け取り、他のコンテナは完全に隔離された状態になります。

よくある実践的なシナリオ

全サブドメインでHTTPをHTTPSにリダイレクト

各プロキシホストのSSL設定でForce SSLを有効にするだけで十分です。NPMが自動的に301リダイレクトを処理します。

内部サブドメインにBasic認証を追加する

Access Listsタブ → ユーザー名とパスワードを設定した新しいリストを作成 → 保護したいプロキシホストに割り当てます。独自の認証機能を持たないGrafana、Kibana、その他の内部ツールに便利です。

SSLの自動更新

NPMは内部のcronjobで証明書を自動更新します。手動Certbotのセットアップと異なり、systemctl status certbot.timerを確認するなどの追加作業は一切不要です。

バックアップとリストア

すべてのデータ(設定、証明書、データベース)はマウントした./data./letsencryptディレクトリに保存されています。バックアップはシンプルです。定期的なオフサイトバックアップが必要な場合は、Sidecar ContainerでS3やGoogle Driveへ自動バックアップする方法も検討してみてください:

tar -czf npm-backup-$(date +%Y%m%d).tar.gz ~/npm/data ~/npm/letsencrypt

注意事項

  • ポート81は一時的にのみ開放するか、IPで制限してください。設定完了後はファイアウォールでポート81を閉じ、管理が必要なときだけ開放するようにしましょう。なお、DockerはデフォルトでUFWのルールをバイパスするため、iptablesレベルでの制御も合わせて確認することをお勧めします。
  • Let’s Encryptのレート制限:同一ドメインに対して7日間で最大5枚の新規証明書しか発行できません。テスト中に何度も試さず、先にステージング環境を使いましょう。
  • ワイルドカードSSL:NPMはワイルドカード証明書(*.yourdomain.com)をサポートしていますが、DNSチャレンジによる検証が必要で、DNSプロバイダー(Cloudflare、AWS Route53など)の追加設定が必要です。
  • バージョン固定:本番環境ではjc21/nginx-proxy-manager:latestを特定のタグに置き換え、更新時の破壊的変更を防ぎましょう。

Share: