Dify は、LLM を使ったチャットボットやワークフローを画面上で組めるツールです。公式の Docker Compose を使えば数コマンドで起動できますが、初期設定のまま VPS で動かすと、インターネットに開いてしまうポートがあります。この記事では、公式の手順で構築したうえで、そのポートを閉じて Caddy で HTTPS 公開するところまでを解説します。
手順は 2026年10月21日 時点の最新版である Dify 1.17.1 で確かめました。
必要なスペック
公式ドキュメントの要件は CPU 2コア以上、メモリ 4GiB 以上です。手元で測ったところ、起動して何もしていない状態で約1.93GiB、再起動の直後には合計で約2.13GiB を使いました。2GB のプランでは足りません。
測り方とコンテナごとの内訳は、Dify に必要なメモリは何GBかで詳しく解説しています。
手順1:VPS に Docker を入れる
Docker のインストール、ファイアウォール、DNS の設定は、VPSにDocker ComposeでアプリをHTTPS公開する基本手順と同じです。公式ドキュメントは Docker Compose 2.24.0 以上を求めています。docker compose version で確認してください。
手順2:公式の手順で Dify を起動する
公式の手順は、最新のリリースのタグを指定してリポジトリを取得する方法です。この記事では、確かめた版を明示するためにタグを直接書いています。
git clone --branch 1.17.1 https://github.com/langgenius/dify.git
cd dify/docker
cp .env.example .env
docker compose up -d
docker compose ps
docker compose ps を実行すると、既定の設定(ベクトルDB は Weaviate、DB は PostgreSQL)では 16個のコンテナが並びます。そのうち init_permissions は初期設定用で、処理が終わると停止するのが正常です。
| 役割 | コンテナ |
|---|---|
| アプリ本体 | api、api_websocket、worker、worker_beat、web、agent_backend |
| データベースとキャッシュ | db_postgres、redis |
| ベクトルDB | weaviate |
| サンドボックス・プロキシ・プラグイン | sandbox、local_sandbox、plugin_daemon、ssrf_proxy、agent_ssrf_proxy、nginx |
| 初期設定(終了する) | init_permissions |
手順3:初期設定のままでは開いているポートを閉じる
起動した状態で、どのポートがサーバーの外に公開されているかを確認します。
docker compose ps --format '{{.Service}}\t{{.Ports}}'
手元の Dify 1.17.1 では、2つのサービスの合わせて3つのポートが、すべての接続元(0.0.0.0)に公開されていました。
| サービス | ポート | 用途 |
|---|---|---|
nginx |
80、443 | Dify の画面と API |
plugin_daemon |
5003 | プラグインのリモートデバッグ |
ufw で塞いでも届きます
Docker 公式ドキュメントによると、Docker で公開したポートへの通信は ufw のルールが適用される前に振り分けられます。ufw で 5003番を許可していなくても、外から届く状態になります。プラグインを開発しないなら、このポートを公開する必要はありません。
.env の EXPOSE_PLUGIN_DEBUGGING_PORT を書き換えて閉じたくなりますが、手元の 1.17.1 の docker-compose.yaml では、同じ変数がプラグインのリモートインストール用ポートの設定にも使われていました。127.0.0.1:5003 のような値を入れると、そちらにも同じ値が渡ってしまいます。
そこで、公式のファイルは書き換えず、上書き用のファイルでポートの公開だけを外します。dify/docker に、次の内容で docker-compose.override.yaml を作ります。
services:
nginx:
ports: !reset []
plugin_daemon:
ports: !reset []
!reset は、元のファイルで設定された値を取り消す Docker Compose の書き方です。このファイル名は docker compose が自動で読み込むため、起動のコマンドは変わりません。
docker compose up -d
docker compose ps --format '{{.Service}}\t{{.Ports}}'
これで、サーバーの外に公開されるポートはなくなります。nginx の 80番と443番も閉じますが、次の手順で Caddy を Dify のネットワークに参加させるため問題ありません。
手順4:Caddy を前に置いて HTTPS で公開する
Dify とは別のディレクトリ(例:~/caddy)に、次のファイルを作ります。Caddy を Dify の Compose が作るネットワーク(dify/docker で起動した場合は docker_default)に参加させ、Dify の nginx へ転送します。
compose.yaml:
services:
caddy:
image: caddy:2.11.7
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "443:443/udp"
environment:
- DIFY_DOMAIN=${DIFY_DOMAIN}
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
- caddy_config:/config
networks:
- dify
networks:
dify:
external: true
name: docker_default
volumes:
caddy_data:
caddy_config:
Caddyfile:
{$DIFY_DOMAIN} {
reverse_proxy nginx:80
}
.env:
DIFY_DOMAIN=dify.example.com
ネットワーク名は docker network ls で確認できます。Dify を別の名前のディレクトリで起動した場合は、「ディレクトリ名_default」になります。
docker compose up -d
ブラウザで https://dify.example.com/install を開き、管理者アカウントを作成します。
手元で確かめた結果
ドメインの代わりに dify.localhost と Caddy の内部証明書を使って、同じ構成を動かしました。
| 確認したこと | 結果 |
|---|---|
| Caddy 経由の HTTPS で Dify の画面が開くか | ログイン画面が日本語で表示された |
| Caddy 経由で API が応答するか | /console/api/setup がステータス 200 を返した |
| HTTP で開いたときの動き | HTTPS へ転送された(ステータス 308) |
| ホストから 80番、5003番に直接届かないか | どちらも接続できなかった |
アップデートするときの注意
公式ドキュメントは、アップデートの手順は版によって異なるため、リリースページで対象の版の案内を確認するよう求めています。あわせて、次の2点を挙げています。
- 新しい版の
.env.exampleと手元の.envを比べ、追加や変更された変数を反映する docker-compose.yamlを書き換えていた場合は、古いファイルを使い続けず、新しい版のファイルに変更を入れ直す(新しい版で必要なサービスが増えることがあるため)
実際に 1.16.1 から 1.18.0 まで上げた手順と注意点は、Dify のアップデート手順にまとめています。
この記事の方法なら、公式の docker-compose.yaml は書き換えていません。アップデートで取り込み直す必要があるのは、上書き用の docker-compose.override.yaml だけです。ただし、新しい版でポートを公開するサービスが増えていないかは、アップデートのたびに docker compose ps で確認してください。