
Difyとは、チャットボット・ワークフロー・ナレッジ検索(RAG)をブラウザ上のGUIで組み立てられる、オープンソースのLLMアプリ開発プラットフォームです。クラウド版(dify.ai)と、自社サーバーで動かすセルフホスト版(Community Edition)があり、後者はDocker Composeの一式が公式リポジトリに同梱されています。ライセンスは執筆時点でApache 2.0に独自条件を加えたもので、「マルチテナントのサービスとして運用しない」「コンソールのロゴと著作権表示を消さない」の2点を守れば、社内利用は無償でできます。
ここではUbuntu 24.04にDifyをセルフホストし、Ollamaのモデルを登録して、ITヘルプデスク向けのチャットボットとワークフロー、ナレッジ検索を作るところまでを進めます。Difyは執筆時点のComposeで15個前後のコンテナが立ち上がる構成なので、「どのコンテナが何をしていて、どこにデータが残るか」を先に押さえてから手を動かします。
この記事でわかること
- クラウド版とセルフホストの違いと、どちらを選ぶかの判断軸
docker compose up -dの前に .env で必ず変える5つの値(SECRET_KEY、INIT_PASSWORD、DBとRedisのパスワードなど)- LinuxのDockerからホストのOllamaに到達できない理由と、plugin_daemonまで含めた解決策
- チャットボット、質問分類つきワークフロー、ナレッジ(General / Parent-child)の作り方
- volumes ディレクトリのバックアップ、リリースタグ単位の更新、公開範囲とAPIキーの管理
Difyでできること、クラウド版とセルフホストの違い
Difyのアプリは4種類です。単発の質問に答える「チャットボット」、ノードを線でつないで分岐や外部API呼び出しを組む「ワークフロー」(会話型は「チャットフロー」)、ツールを自律的に呼ぶ「エージェント」、そしてプロンプトだけで完結する「テキスト生成」です。どのアプリも「公開」するとWebアプリのURL、他サイトへの埋め込みコード、REST APIの3つの形で使えるようになります。文書を登録して検索させる「ナレッジ」はアプリから参照する共通部品です。
| 観点 | クラウド版(dify.ai) | セルフホスト(Community Edition) |
|---|---|---|
| データの所在 | Dify社の環境。プロンプト・ナレッジ・会話ログが社外に置かれる | 自社サーバーの docker/volumes 配下 |
| ローカルLLM(Ollama)への接続 | 社内ネットワークに到達できないため実質不可 | 同一LAN内のOllamaを直接登録できる |
| 更新作業 | 不要 | リリースタグごとに手動。マイグレーションの確認が要る |
| 費用の考え方 | プラン課金(最新は公式サイトで確認) | サーバーと運用工数。ソフトウェアは無償 |
| 向く判断 | まず触って評価したい、社外秘のデータを入れない | 社内文書を扱う、ローカルLLMを使う、監査要件がある |
情シスの立場で社内文書を扱うなら、判断はセルフホスト一択に近いです。例外は「社外秘を一切入れない前提で、UIの使い勝手を1週間だけ試す」ケースで、その場合はクラウド版でアプリを作ってDSLファイルとしてエクスポートし、あとでセルフホスト側にインポートできます。
必要スペックと前提
公式の最低要件はCPU 2コア、メモリ4GiBで、Docker Composeは2.24.0以降が必要です(執筆時点)。ただし実際に立ち上がるのは、コアサービスの api、api_websocket、worker、worker_beat、web、plugin_daemon、agent_backend に加えて、db_postgres、redis、weaviate(既定のベクトルDB)、nginx、ssrf_proxy、agent_ssrf_proxy、sandbox、local_sandbox という依存コンポーネント群です。これに社内利用の同時アクセスが乗るので、4コア・8GB以上を確保したほうが、起動直後の502や取り込み時のワーカー停止で悩まずに済みます。LLM自体はDifyの外(OllamaかクラウドAPI)で動くため、GPUはDifyのホストには不要です。
docker compose version # 期待: Docker Compose version v2.24.0 以上
nproc && free -h # コア数とメモリ
df -h /srv # volumes を置く領域。ナレッジ用に数十 GB は見ておくdocker compose での起動手順
公式ドキュメントは main ブランチではなく、最新のリリースタグを取得する手順を案内しています。main は開発中の変更を含み、.env.exampleとcomposeの整合が取れていないことがあるからです。
cd /srv
# 最新リリースタグを GitHub API から取ってクローン(jq が必要)。固定したいなら --branch 1.x.y と直書き
git clone --branch "$(curl -s https://api.github.com/repos/langgenius/dify/releases/latest | jq -r .tag_name)" https://github.com/langgenius/dify.git
cd dify/docker
cp .env.example .env起動前に .env で変える値を挙げます。既定値のまま動かしても立ち上がりますが、DBとRedisのパスワードは difyai123456、サンドボックスのAPIキーは dify-sandbox という公開済みの文字列なので、社内LANでも変えてから起動します。起動後に変えると各コンテナの設定がずれて接続エラーになるため、順番が大切です。
# セッション Cookie と JWT の署名、OAuth 資格情報の暗号化に使う。空なら自動生成されるが、明示して控えておく
SECRET_KEY=(openssl rand -base64 42 の出力)
# /install ページを開くときに要求されるパスワード。先に誰かに管理者を作られる事故を防ぐ
INIT_PASSWORD=(推測されない文字列)
# 依存コンポーネントの既定パスワードを差し替える
DB_PASSWORD=(変更)
REDIS_PASSWORD=(変更)
SANDBOX_API_KEY=(変更)
PLUGIN_DAEMON_KEY=(変更)
# 公開ポート。ホストの 80 番が使用中なら変える。127.0.0.1:8080 のように書くと loopback だけに束縛できる
EXPOSE_NGINX_PORT=80
EXPOSE_NGINX_SSL_PORT=443
# 社内 URL を決めたら揃えておく(メールの招待リンクや API のベース URL に使われる)
CONSOLE_WEB_URL=https://dify.corp.example
APP_WEB_URL=https://dify.corp.example
SERVICE_API_URL=https://dify.corp.exampledocker compose up -d
docker compose ps # すべて Up になるまで 1〜3 分。api はマイグレーション後に起動する
docker compose logs -f api # "Running migrations" のあと待ち受け開始のログが出るブラウザで http://サーバーのIP/install を開き、INIT_PASSWORDを入力してから管理者アカウント(メールアドレス・名前・パスワード)を作ります。以後は http://サーバーのIP/ がログイン画面です。追加メンバーは「設定 → メンバー」から招待しますが、SMTP(MAIL_TYPE など)を設定していない場合は招待メールが飛ばないので、画面に表示される招待リンクをコピーして渡します。
モデルプロバイダーの登録(Ollama / OpenAI互換)
Dify 1.x以降、モデルプロバイダーはプラグインとして「設定 → モデルプロバイダー」からマーケットプレイス経由でインストールします(MARKETPLACE_ENABLED=true のとき)。インターネットに出られないサーバーでは、プラグインの .difypkg ファイルを別環境でダウンロードしてアップロードします。署名検証(FORCE_VERIFYING_SIGNATURE=true)は自作プラグインを入れるとき以外は切らないでください。
Ollamaプラグインの登録フォームは、モデル名(gemma4:12b のようにOllama側のタグそのまま)、Base URL、モデルタイプ(Chat か Text Embedding)、モデルのコンテキスト長、最大トークン数の上限、Vision対応、Function call対応の各項目です。チャット用の gemma4:12b と、ナレッジの索引用に bge-m3(Text Embedding)の2つを登録し、「システムモデル設定」でそれぞれを既定に指定します。
ここで最も多いつまずきがBase URLです。Difyはコンテナ内で動くので、http://localhost:11434 はコンテナ自身を指し、ホストのOllamaには届きません。macOSやWindowsのDocker Desktopなら http://host.docker.internal:11434 で解決しますが、Linuxでは執筆時点のcomposeに extra_hosts の定義がなく、この名前は解決されません。解決策は2つあり、ホストのLAN IP(例: http://192.168.1.20:11434)を直接書くか、上書きファイルで名前を足します。
# dify/docker に置くと docker compose が自動で読み込む
services:
api:
extra_hosts: ["host.docker.internal:host-gateway"]
worker:
extra_hosts: ["host.docker.internal:host-gateway"]
plugin_daemon:
extra_hosts: ["host.docker.internal:host-gateway"]モデルプロバイダーがプラグイン化された執筆時点の構成では、Ollamaへ実際にHTTPリクエストを送るのは api ではなく plugin_daemon コンテナです。api にだけ extra_hosts を足して「疎通できるのに接続エラー」になるのはこれが原因です。上書きファイルでは3サービスすべてに足しています。あわせてOllama側は OLLAMA_HOST=0.0.0.0 で待ち受け、ufwでDockerのサブネット以外からの11434番を拒否します。手順はOllama入門の社内公開の節と同じです。
vLLMやLM Studio、社内のAPIゲートウェイを使う場合は「OpenAI-API-compatible」プラグインで、Base URLとモデル名、APIキーを登録します。クラウドAPI(OpenAI、Anthropic、Azure OpenAIなど)は各プロバイダーのプラグインにAPIキーを入れるだけです。
チャットボットアプリの作成
「スタジオ → 最初から作成 → チャットボット」で作ります。オーケストレーション画面の左がプロンプト、右がプレビューです。ITヘルプデスクなら、プロンプトは次の程度で十分に動きます。
あなたは社内ITヘルプデスクの一次回答担当です。
- 回答は敬語で、手順は番号付きで 5 ステップ以内にまとめてください。
- 「コンテキスト」に該当する記載があればそれを優先し、出典の文書名を最後に書いてください。
- 該当がない場合は推測せず、「情シスへ問い合わせてください(内線 1234)」と案内してください。
- パスワードや個人情報の入力は求めないでください。プロンプト内の「コンテキスト」は、画面下の「コンテキスト」にナレッジを追加すると自動で埋まる部分です。モデルはヘッダー右のセレクタで gemma4:12b を選び、温度は0.3前後に下げると手順の再現性が上がります。右側のプレビューで数問試したら「公開する」を押し、Webアプリとしての共有URL、埋め込みコード、APIアクセスのいずれかを選びます。次のAPI呼び出しにある "user": "ryoma" は検証で使った識別子なので、呼び出し元の利用者を表す文字列に置き換えます。
# アプリの「API アクセス」で発行したキー(app- で始まる)で呼ぶ。user はログ上の識別子
curl -X POST http://localhost/v1/chat-messages \
-H "Authorization: Bearer app-xxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"inputs": {}, "query": "VPNに繋がりません", "response_mode": "blocking", "user": "ryoma"}'ワークフローの基本例:問い合わせを分類して部門別ナレッジで答える
会話を続けたいなら「チャットフロー」、フォーム入力から結果を1回返すなら「ワークフロー」を選びます。ヘルプデスクの例では、質問をIT・総務・その他に振り分け、部門ごとのナレッジで検索してから回答させます。
- 開始ノード:ユーザーの質問は
sys.queryとして自動で入る - 質問分類器ノード:クラスを「IT機器・アカウント」「総務・経費」「その他」の3つ定義し、分類に使うモデルを指定する
- 知識検索ノード(クラスごとに1つ):対応する部門ナレッジを選び、取得件数を5〜8に設定する
- LLMノード:システムプロンプトに前節の文面を入れ、コンテキストに知識検索ノードの結果変数を指定する
- 回答ノード:LLMノードの出力を返す。「その他」分岐はLLMを通さず固定文で窓口を案内する
右上の「デバッグとプレビュー」で実行すると、ノードごとの入出力と所要時間がトレースとして残ります。「分類が外れた」のか「検索で拾えなかった」のか「LLMが根拠を無視した」のかがこの画面で切り分けられるので、精度の議論はトレースを見ながら行います。HTTPリクエストノードでチケットシステムのAPIを叩いて起票する、コードノード(サンドボックス内のPython)で整形する、といった拡張もここから足します。
ナレッジ(RAG)機能
「ナレッジ → ナレッジを作成」でファイル(PDF、Word、Markdown、テキストなど)をアップロードすると、チャンク設定の画面になります。執筆時点の選択肢は次のとおりです。
| 設定 | 選択肢 | 推奨と理由 |
|---|---|---|
| チャンクモード | General(全チャンク同一設定)/ Parent-child(小さな子チャンクで検索し、大きな親チャンクを渡す) | 規程・マニュアルはParent-child。条文単位でヒットさせつつ、回答には章全体を渡せる |
| インデックス方式 | 高品質(埋め込みモデルでベクトル化)/ 経済的(キーワードのみ、埋め込み不要) | 高品質。Parent-childは高品質のみ対応。経済的は埋め込みモデルがない環境の暫定策 |
| 前処理 | 連続する空白・改行の正規化、URLとメールアドレスの除去 | 正規化は有効。URL除去は社内リンクを根拠に使うなら無効 |
| 検索設定 | ベクトル / 全文 / ハイブリッド、取得件数、スコア閾値、再ランク | ハイブリッド + 取得件数5〜8。再ランクモデルがあれば有効化 |
取り込み後は「検索テスト」タブで質問を入れ、どのチャンクがどのスコアで返るかを確認します。ここで期待する条文が出ないなら、チャンク設定を変えて再取り込みします。埋め込みモデルはナレッジ作成時に決まり、あとから変えると再作成になります。
権限は「ナレッジ単位でどのメンバーに見せるか」で制御します。文書1件ごとに閲覧者を変えることはできないので、部門や機密区分ごとにナレッジを分ける運用が前提です。文書単位のACLや、ファイルサーバー側の権限との自動連携が要件なら、pgvectorで権限フィルタ付きRAGを自作するほうが要件に合います。設計の背景はRAGの仕組みと設計原則にまとめています。
更新・バックアップ・セキュリティ
永続データはすべて dify/docker/volumes 配下です。db/data(PostgreSQL:アプリ定義、会話ログ、メンバー)、redis/data、weaviate(ベクトル索引)、app/storage(アップロード原本)、plugin_daemon(インストール済みプラグイン)が主で、これに .env と上書きファイルを加えた一式がバックアップ対象です。DBデータはコンテナ内のユーザー所有なので、ホスト側では sudo で固めます。
cd /srv/dify/docker
docker compose down # 停止してから取る。稼働中の db/data をコピーすると壊れる
sudo tar czf /srv/backup/dify-$(date +%F).tgz .env docker-compose.override.yaml volumes/
docker compose up -d
# 更新(リリースノートの upgrade guide を先に読む)
docker compose down
git fetch --tags
git checkout 1.x.y # 新しいリリースタグ
diff .env.example .env # 追加された変数を .env に反映する
docker compose pull
docker compose up -d # api が起動時にマイグレーションを実行する更新前のバックアップは省略できません。api は起動時にデータベースのマイグレーションを流し、これは基本的に一方向です。更新後に不具合が出て前のタグに戻しても、スキーマは戻りません。公式ドキュメントもリリースごとに手順が異なるとして、対象バージョンのアップグレードガイドを読むよう案内しています。
公開範囲は3層で考えます。まず80番をインターネットに向けないこと。社内LAN限定でも、前段にTLSを終端するnginxやCaddyを置き、EXPOSE_NGINX_PORT=127.0.0.1:8080 のようにDify側のnginxをループバックだけに束縛すると、証明書とアクセス制御をDifyの外で完結できます。次にアプリのAPIキー(app- で始まる)はアプリごとに発行し、利用側のシークレット管理に置いて共有チャットに貼らないこと。漏れたら該当アプリのキーだけ再発行すれば済むよう、アプリ横断で1本のキーを使い回さないでください。最後にメンバーのロールです。ワークスペースの管理者ロールはモデルプロバイダーのAPIキーを見られる立場なので、情シス側の数名に限定します。
会話ログには利用者の質問文がそのまま残ります。ログを誰がどの目的で見るかは、Difyの設定ではなく社内ガイドライン側で先に決めておく事項です。
動作確認
$ docker compose ps --format "table {{.Service}}\t{{.Status}}" | sort
SERVICE STATUS
api Up 5 minutes
api_websocket Up 5 minutes
db_postgres Up 5 minutes (healthy)
nginx Up 5 minutes
plugin_daemon Up 5 minutes
redis Up 5 minutes (healthy)
weaviate Up 5 minutes
worker Up 5 minutes
...
$ curl -s -o /dev/null -w "%{http_code}\n" http://localhost/signin
200
$ docker compose exec plugin_daemon curl -s http://host.docker.internal:11434/
Ollama is running ← plugin_daemon から Ollama に到達できている最後の1行がOllama連携の要です。api ではなく plugin_daemon から確認する点だけ覚えておいてください(コンテナに curl がない版なら wget -qO- で代用します)。UI側では、モデルプロバイダー画面で登録したモデルが「利用可能」になっていること、ナレッジの検索テストでチャンクが返ること、チャットボットのプレビューで回答に出典が付くことの3点を見て完了です。
つまずきポイント
起動直後にブラウザで 502 Bad Gateway
起動から数分は api がマイグレーション中で、nginxの転送先がまだ応答していない状態です。docker compose logs -f api で起動完了を待ちます。長時間続く場合は、コンテナ再作成後にnginxが古いコンテナIPを掴んでいることがあり、docker compose restart nginx で解消します。
Ollamaの登録で「Connection refused」「HTTPConnectionPool(host='127.0.0.1', port=11434)」
Base URLがコンテナ自身を指しています。LAN IPか host.docker.internal(上書きファイルで3サービスに追加済みであること)に変え、Ollama側が 0.0.0.0 で待ち受けているかを ss -ltnp | grep 11434 で確認します。
ドメインやポートを変えた後、ログイン直後に 401 が続く
公式トラブルシューティングにある典型で、CONSOLE_API_URL、CONSOLE_WEB_URL、APP_WEB_URL、SERVICE_API_URL と、CORS系の CONSOLE_CORS_ALLOW_ORIGINS、WEB_API_CORS_ALLOW_ORIGINS を新しいURLに揃えてから docker compose down && docker compose up -d します。コンソールとアプリを別サブドメインに分ける場合は COOKIE_DOMAIN と NEXT_PUBLIC_COOKIE_DOMAIN も設定します。
ナレッジに大きなPDFをアップロードすると拒否される
アプリ側の UPLOAD_FILE_SIZE_LIMIT とnginx側の NGINX_CLIENT_MAX_BODY_SIZE を同じ値に上げます。片方だけ上げると、413か「ファイルサイズ超過」のどちらかで止まり続けます。
管理者のパスワードを忘れた
docker exec -it docker-api-1 flask reset-password を実行し、メールアドレスと新しいパスワードを対話で入力します。コンテナ名はプロジェクト名で変わるので、docker compose ps で確認してください。
よくある質問
Difyは無料で使えますか?
セルフホスト版はソフトウェア自体が無償です。ライセンスはApache 2.0に「マルチテナントのサービス運用には別途ライセンスが必要」「コンソールのロゴと著作権表示を消さない」という条件が付いたもので、自社内の利用なら該当しません。クラウド版の料金プランは公式サイトで確認してください。
ローカルLLMと組み合わせられますか?
できます。Ollamaプラグインでチャット用モデルと埋め込みモデルの両方を登録でき、vLLMなどはOpenAI互換プラグインで繋がります。注意点はネットワークだけで、Linuxでは plugin_daemon を含む3サービスに host.docker.internal を定義し、Ollamaを 0.0.0.0 で待ち受けさせる必要があります。
本番運用で注意すべき点は?
起動前に既定パスワードとSECRET_KEY、INIT_PASSWORDを変えること、80番を外に向けず前段のプロキシでTLSを終端すること、更新はリリースタグ単位でバックアップ後に行うこと、アプリのAPIキーをアプリごとに分けて管理すること、会話ログの閲覧ルールを先に決めることの5点です。
まとめ
Difyのセルフホストは、リリースタグを取得して .env の既定パスワードとSECRET_KEY、INIT_PASSWORDを変えてから docker compose up -d する、という順番が守れていれば大きく外しません。Ollama連携の落とし穴はLinuxでの名前解決と、リクエストを出すのが plugin_daemon である点の2つで、上書きファイルで3サービスに extra_hosts を足せば解決します。ナレッジはナレッジ単位の権限で運用し、文書単位のACLが必要になった時点で自作RAGに切り替える判断をしてください。
次に読む
- Ollamaで始めるローカルLLM入門:Difyから呼ぶモデルの選び方と
OLLAMA_HOSTの設定 - Docker Compose入門:override ファイル、volumes、.env の基礎
- RAGとは?仕組みと社内導入で失敗しない設計原則:ナレッジ機能で足りるかを判断する材料