Difyバージョン1.10.1の構築時にハマった話

はじめに
対象読者
本記事は、ノーコードAIアプリ開発ツールで人気のある Dify を構築・運用している方を主な対象としています。
本記事の前提知識として、Dockerに関する基本的な知識があることを想定しています。
記事のゴール
Difyのバージョン1.10.1を新規構築する際に発生する問題を通じて、コンテナ実行ユーザー変更というセキュリティ強化の背景を理解し、今後のデプロイに活かせるようになること。
Difyバージョン1.10.1の構築時に問題に遭遇
発生した問題
Difyの構築は今まで何度か経験があるのですが、先日、公式のdocker-composeデプロイ手順に沿って AWS EC2 上にDifyを新規構築していた際にトラブルに遭遇しました。docker compose up -d でDifyを起動後、Difyを実行しているEC2へブラウザでアクセスすると、初回アクセス時は以下のようなセットアップ画面が表示されます。

普段はこの画面で管理者アカウントの設定をしてセットアップを継続するのですが、セットアップボタンをクリックしたところ、以下のような見慣れないエラーが表示され、継続することができませんでした。

Setup failed: PermissionDenied (persistent) at write => permission denied Context: service: fs path: privkeys/e5ddbea6-0aac-4839-ad09-3548ae84cb6b/private.pem Source: Permission denied (os error 13)
以前と全く同じ手順で構築していたので「なんでだろ?」と思いつつも、調査を進めていくことにします。
問題の原因を調査
エラーメッセージを見ると、典型的なファイル書込み権限の問題のように見えます。
また、privkeys/e5ddbea6-0aac-4839-ad09-3548ae84cb6b/private.pem というファイル名から察するに、コンテナ内で一時的に利用するようなものではなく、永続化したいファイルと予想できます。
永続したいファイルであれば、コンテナのマウント先のホストボリュームのどこかに保存されるだろう。
…というところまでは分かったのですが、このエラーメッセージでは、Difyを構成しているどのサービス(コンテナ)がどこにファイルを書込みしようとしているのかさっぱり分かりません。
ということで、コンテナログ側に何か出ていないか見てみたところ、それっぽいログがありました。
$ docker compose logs
...(中略)...
api-1 | 2025-12-02 07:10:17.696 ERROR [Dummy-1] [account_service.py:1295] - Setup account failed, email: xxxx@creationline.com, name: xxxx
api-1 | Traceback (most recent call last):
api-1 | File "/app/api/services/account_service.py", line 1283, in setup
api-1 | TenantService.create_owner_tenant_if_not_exist(account=account, is_setup=True)
api-1 | File "/app/api/services/account_service.py", line 1028, in create_owner_tenant_if_not_exist
api-1 | tenant = TenantService.create_tenant(name=f"{account.name}'s Workspace", is_setup=is_setup)
api-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
api-1 | File "/app/api/services/account_service.py", line 1000, in create_tenant
api-1 | tenant.encrypt_public_key = generate_key_pair(tenant.id)
api-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
api-1 | File "/app/api/libs/rsa.py", line 22, in generate_key_pair
api-1 | storage.save(filepath, pem_private)
api-1 | File "/app/api/extensions/ext_storage.py", line 89, in save
api-1 | self.storage_runner.save(filename, data)
api-1 | File "/app/api/extensions/storage/opendal_storage.py", line 44, in save
api-1 | self.op.write(path=filename, bs=data)
api-1 | opendal.exceptions.PermissionDenied: PermissionDenied (persistent) at write => permission denied
api-1 |
api-1 | Context:
api-1 | service: fs
api-1 | path: privkeys/74ebf043-785a-4fac-a8df-b2a480253b4e/private.pem
api-1 |
api-1 | Source:
api-1 | Permission denied (os error 13)
どうも、apiというサービス(コンテナ)がこのエラーを出力しているようですが、どのパスに書き込もうとしてエラーになっているのかが良く分かりません。
ということで、当該コンテナの詳細を見ていきます。
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5a2eb3fbfe52 nginx:latest "sh -c 'cp /docker-e…" 15 minutes ago Up 15 minutes 0.0.0.0:80->80/tcp, [::]:80->80/tcp, 0.0.0.0:443->443/tcp, [::]:443->443/tcp docker-nginx-1
9030e6c5a654 langgenius/dify-api:1.10.1 "/bin/bash /entrypoi…" 15 minutes ago Up 15 minutes 5001/tcp docker-api-1
...(中略)...
$ docker inspect docker-api-1
...(中略)...
"Mounts": [
{
"Type": "bind",
"Source": "/home/ubuntu/dify/docker/volumes/app/storage", ←★ここ
"Destination": "/app/api/storage", ←★ここ
"Mode": "rw",
"RW": true,
"Propagation": "rprivate"
}
],
...(中略)...
ホスト側のパス /home/ubuntu/dify/docker/volumes/app/storage が、コンテナの /app/api/storage というパスにマウントされているようです。
ということでホスト側のパスのオーナー、パーミッションなどを確認してみます。
$ ls -l total 344 -rw-rw-r-- 1 ubuntu ubuntu 7070 Dec 2 07:37 README.md drwxrwxr-x 2 ubuntu ubuntu 4096 Dec 2 07:37 certbot drwxrwxr-x 2 ubuntu ubuntu 4096 Dec 2 07:37 couchbase-server -rw-rw-r-- 1 ubuntu ubuntu 30971 Dec 2 07:37 docker-compose-template.yaml -rw-rw-r-- 1 ubuntu ubuntu 10107 Dec 2 07:37 docker-compose.middleware.yaml -rw-rw-r-- 1 ubuntu ubuntu 174447 Dec 2 07:37 docker-compose.png -rw-rw-r-- 1 ubuntu ubuntu 69181 Dec 2 07:37 docker-compose.yaml drwxrwxr-x 2 ubuntu ubuntu 4096 Dec 2 07:37 elasticsearch -rwxrwxr-x 1 ubuntu ubuntu 4097 Dec 2 07:37 generate_docker_compose -rw-rw-r-- 1 ubuntu ubuntu 7581 Dec 2 07:37 middleware.env.example drwxrwxr-x 4 ubuntu ubuntu 4096 Dec 2 07:37 nginx drwxrwxr-x 2 ubuntu ubuntu 4096 Dec 2 07:37 pgvector drwxrwxr-x 2 ubuntu ubuntu 4096 Dec 2 07:37 ssrf_proxy drwxrwxr-x 2 ubuntu ubuntu 4096 Dec 2 07:37 startupscripts drwxrwxr-x 3 ubuntu ubuntu 4096 Dec 2 07:37 tidb drwxrwxr-x 12 ubuntu ubuntu 4096 Dec 2 07:37 volumes ←★ここ
オーナーがUbuntu、パーミッションが775となっていました。
「うーん、以前も確かこんな感じだったし、なんでだろう?」と思いながらちょっと悩みますが、単純に権限が無いんだろうということに立ち返り、どのユーザーで実行されているかを見てみます。
$ docker exec -it docker-api-1 id uid=1001(dify) gid=1001(dify) groups=1001(dify)
uid=1001 というユーザーで実行されいることが分かりました。
「あ、なるほど」と思いつつ、念のためホスト側で現在のユーザーUbuntuのuidを確認してみたところ、uid=1000 となっていて、やはり相違していることが分かりました。
$ id uid=1000(ubuntu) gid=1000(ubuntu) groups=1000(ubuntu),4(adm),24(cdrom),27(sudo),30(dip),105(lxd),988(docker)
コンテナが uid=1001 のユーザーで実行されているため、オーナーが uid=1000 となっている volumes ディレクトリへ書き込みができないというのが答えのようです。
ということで、オーナーを変更してDifyを再起動してみることにします。
$ sudo chown -R 1001:1001 volumes $ ls -l total 344 -rw-rw-r-- 1 ubuntu ubuntu 7070 Dec 2 07:37 README.md drwxrwxr-x 2 ubuntu ubuntu 4096 Dec 2 07:37 certbot drwxrwxr-x 2 ubuntu ubuntu 4096 Dec 2 07:37 couchbase-server -rw-rw-r-- 1 ubuntu ubuntu 30971 Dec 2 07:37 docker-compose-template.yaml -rw-rw-r-- 1 ubuntu ubuntu 10107 Dec 2 07:37 docker-compose.middleware.yaml -rw-rw-r-- 1 ubuntu ubuntu 174447 Dec 2 07:37 docker-compose.png -rw-rw-r-- 1 ubuntu ubuntu 69181 Dec 2 07:37 docker-compose.yaml drwxrwxr-x 2 ubuntu ubuntu 4096 Dec 2 07:37 elasticsearch -rwxrwxr-x 1 ubuntu ubuntu 4097 Dec 2 07:37 generate_docker_compose -rw-rw-r-- 1 ubuntu ubuntu 7581 Dec 2 07:37 middleware.env.example drwxrwxr-x 4 ubuntu ubuntu 4096 Dec 2 07:37 nginx drwxrwxr-x 2 ubuntu ubuntu 4096 Dec 2 07:37 pgvector drwxrwxr-x 2 ubuntu ubuntu 4096 Dec 2 07:37 ssrf_proxy drwxrwxr-x 2 ubuntu ubuntu 4096 Dec 2 07:37 startupscripts drwxrwxr-x 3 ubuntu ubuntu 4096 Dec 2 07:37 tidb drwxrwxr-x 12 1001 1001 4096 Dec 2 07:37 volumes ←★オーナーが変更された $ docker compose down ...(中略)... $ docker compose up -d ...(中略)...
Difyの再起動後、再度ブラウザでアクセスして管理者アカウントの設定を行い、セットアップボタンをクリックしたところ、無事に初期画面にアクセスすることができました。

なぜ急にこのような問題が発生したのか
変更点を調べてみる
ひとまず問題は解決したのですが、なぜ急にこのような問題が発生したのか気になったため、あまり深追いしない程度にもう少し調べてみたいと思います。
まずバージョンが変わったのではないかと思って調べてみます。
前回構築したバージョンを確認してみると 1.10.0 となっていました。
次に、今回構築していたバージョンを確認してみると 1.10.1 となっており、マイナーバージョンが1つ進んでいました。
ここで公式のGitHubを確認してみたところ、2025年11月26日に最新版がバージョン 1.10.0 → 1.10.1 にアップデートされていることが分かりました。
そしてたくさん記載されている変更点をよーく見ていくと、こんなことがさらっと書かれてありました。

なるほど、バージョン1.10.0までは rootユーザーでコンテナを実行していたので、このような問題が発生しなかったわけですね。
皆さんもご存じかと思いますが、rootユーザーでコンテナ実行するのはセキュリティリスクがあるため、本来は推奨されません。
そのため、Dify側もセキュリティ強化の一環でこういった対応をしたんでしょうね。
関連付けられたIssueの詳細を確認してみると、どうも公式ドキュメントへの反映だけがまだ済んでいない状態のようです。

ちらっと確認したところ、以前構築したバージョンをアップデートした場合は、特に問題なく使えるようだったので、この問題に遭遇している人が多くないということで後回しにされてるのかもしれません。
(と思ってたら、この問題に遭遇した人がDiscussionを上げてたようです)
いずれにしてもコミュニティバージョンなので、仕方なしといったところでしょうか。
まとめ
学んだことの整理
今回の記事を通じて、以下の点を学びました。
- エラーの原因はファイルアクセス権限の不足: Difyのセットアップ失敗は、APIコンテナが永続化に必要なファイル(
private.pem)をホストボリュームに書き込む際のPermissionDeniedエラーが原因 - コンテナ実行ユーザーとホストオーナーの不一致: APIコンテナはセキュリティ強化のため
uid=1001(dify)で実行されているのに対し、ホスト側のマウント先ボリュームのオーナーがuid=1000(ubuntu)となっており、書き込みがブロックされていた - バージョンアップでセキュリティ強化: 今回の問題は、Difyがバージョン 1.10.1 にアップデートされた際、コンテナの実行ユーザーが従来の root から 非rootユーザー に変更されたことが理由
今回のトラブルシューティングを通じて、コンテナ技術において「コンテナ内のプロセス実行ユーザー」と「ホスト側のボリュームオーナー」の権限整合性が、デプロイの成否を分ける重要なポイントであることを再認識できました。Difyのセキュリティ強化という前向きな変更がもたらした課題ですが、適切な対応で乗り越えることが可能です。
本記事が、同様の問題に遭遇した開発者の皆さんの迅速な解決の一助となれば幸いです。
