- Docker
Docker bind mountのPermission deniedの直し方
Permission denied(can't create ...: Permission denied)は、コンテナの中で動くユーザーの UID が、ホストのフォルダーに書き込める UID と違うときに起きます。 bind mount(-v ./data:/app/data)では、ホストのフォルダーの持ち主・権限が、そのままコンテナの中に見えます。コンテナの中のユーザー名ではなく、数字の UID で判定されます。
| 状況 | 直し方 |
|---|---|
| イメージのユーザー(例:UID 10001)が、ホストのフォルダー(UID 1001)に書けない | --user "$(id -u):$(id -g)" で、ホストと同じ UID で動かす |
| 毎回同じ UID で使う(開発用のイメージ) | Dockerfile で UID を引数にしてユーザーを作る |
ホストに無いフォルダーを -v で指定した |
Docker が root の持ち主で作るので、先に自分で mkdir |
| 逆に、ホストでファイルを編集・削除できない | コンテナが root で書いた。--user で動かす |
確認環境:Docker Engine 29.4(Linux)、Docker Compose v5.1.3。ホストのユーザーは UID 1001・GID 1002(確認日 2026年10月11日)。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Docker bind mountのPermission deniedの直し方」
再現
FROM (ベースのイメージ)
RUN useradd -u 10001 app && mkdir -p /app/data && chown app:app /app/data
USER app
WORKDIR /app
CMD ["sh", "-c", "id && echo hello > /app/data/out.txt && echo written"]
$ mkdir data
$ docker run --rm -v "$PWD/data:/app/data" app11:1
uid=10001(app) gid=10001(app) groups=10001(app)
sh: can't create /app/data/out.txt: Permission denied
Dockerfile で /app/data を app の持ち主にしていても、bind mount すると、ホストの data フォルダーに置き換わります。
まず確認:ホストとコンテナの UID
$ id ← ホストのユーザー
uid=1001(dev) gid=1002(dev)
$ ls -lnd data ← ホストのフォルダー(-n で数字の UID)
drwxrwxr-x 2 1001 1002 4096 ... data
$ docker run --rm -v "$PWD/data:/app/data" app11:1 ls -lnd /app/data ← コンテナから見たフォルダー
drwxrwxr-x 2 1001 1002 4096 ... /app/data
| 確認すること | 値 |
|---|---|
コンテナで動くユーザー(id) |
UID 10001 |
フォルダーの持ち主(ls -ln) |
UID 1001、権限 rwxrwxr-x(その他の人は書けない) |
コンテナの中でも、フォルダーの持ち主はホストと同じ 1001 のままです。UID 10001 は「その他の人」なので書けません。ls -l(-n なし)だと、コンテナの中では別の名前や数字に見えて分かりにくいので、ls -ln で数字を比べます。
直し方1:--user でホストと同じ UID で動かす
docker run --rm --user "$(id -u):$(id -g)" -v "$PWD/data:/app/data" app11:1
uid=1001 gid=1002 groups=1002
written
$ ls -ln data
-rw-r--r-- 1 1001 1002 6 ... out.txt ← ホストの自分の持ち主で作られた
書き込めて、作られたファイルもホストの自分の持ち主になりました。 ホストでそのまま編集・削除できます。
| 注意 | 確認環境での結果 |
|---|---|
| コンテナの中に、その UID のユーザーが無い | whoami: unknown uid 1001、HOME=/ |
ユーザー名やホームフォルダーを使うアプリ(ホームにキャッシュを書くツールなど)は、ここで別のエラーになることがあります。その場合は直し方2にします。
直し方2:Dockerfile で UID を合わせる
ARG UID=1000
ARG GID=1000
RUN groupadd -g "$GID" app && useradd -u "$UID" -g "$GID" app \
&& mkdir -p /app/data && chown app:app /app/data
USER app
docker build -t app11:uid --build-arg UID="$(id -u)" --build-arg GID="$(id -g)" .
docker run --rm -v "$PWD/data:/app/data" app11:uid
uid=1001(app) gid=1002(app) groups=1002(app)
written
ビルドのときにホストの UID を渡すと、名前のあるユーザーとして、同じ UID で動きました。 開発用のイメージに向いています(本番用のイメージに、開発する人の UID を埋め込むのは避けます)。ベースのイメージによって、ユーザーを作るコマンド(useradd・adduser)の書き方が違います。
docker compose の落とし穴:UID が空だと root で動く
services:
app:
image: app11:1
user: "${UID}:${GID}"
volumes:
- ./data:/app/data
$ docker compose run --rm app
WARN The "UID" variable is not set. Defaulting to a blank string.
WARN The "GID" variable is not set. Defaulting to a blank string.
uid=0(root) gid=0(root) groups=0(root) ← root で動いた
written
$ ls -ln data
-rw-r--r-- 1 0 0 6 ... out.txt ← root の持ち主のファイル
UID は bash では環境変数として渡されないため、compose から見ると空でした。 user: ":" になり、イメージの USER app も無視して root で動きました。エラーにならず書き込めてしまうので、気づきにくい形です。さらに、bash では UID は読み取り専用の変数なので、UID=$(id -u) docker compose run ... と書いても UID: readonly variable で設定できませんでした。
.env ファイルに書くと、確認環境では正しく動きました。
printf "UID=%s\nGID=%s\n" "$(id -u)" "$(id -g)" > .env
docker compose run --rm app
uid=1001 gid=1002 groups=1002
written
.env を使わないなら、HOST_UID のように別の名前の変数にして、export HOST_UID=$(id -u) で渡す方法もあります。
ホストに無いフォルダーを -v で指定したとき
$ docker run --rm -v "$PWD/nohost:/app/data" app11:1
sh: can't create /app/data/out.txt: Permission denied
$ ls -lnd nohost
drwxr-xr-x 2 0 0 4096 ... nohost ← root の持ち主で作られた
-v は、ホストに無いフォルダーを Docker が自動で作ります(Docker 公式ドキュメント)。Docker のデーモンが root で動いているため、root の持ち主のフォルダーになり、コンテナのユーザーも、ホストの自分も書けません。
| 書き方 | ホストに無いとき |
|---|---|
-v ./nohost:/app/data |
root の持ち主で自動作成 |
--mount type=bind,src=./nohost,dst=/app/data |
エラー:bind source path does not exist |
先に mkdir しておくか、間違いに気づきやすい --mount を使います。
逆パターン:ホストでファイルを編集できない
(コンテナを root で動かして、bind mount に書き込んだあと)
$ ls -ln data
-rw-r--r-- 1 0 0 2 ... root.txt
$ echo x >> data/root.txt
bash: data/root.txt: Permission denied
コンテナが root で作ったファイルは、ホストでも root の持ち主になり、編集できませんでした(フォルダーが自分の持ち主なら、削除はできました)。sudo chown -R $(id -u):$(id -g) data で持ち主を戻し、以降は --user で動かします。
なぜ chmod 777 を避けるのか
$ chmod 777 data
$ docker run --rm -v "$PWD/data:/app/data" app11:1
written ← 書けるようになった
$ ls -ln data
-rw-r--r-- 1 10001 10001 6 ... out.txt ← 持ち主は UID 10001
$ echo edit >> data/out.txt
Permission denied ← ホストの自分が編集できない
chmod 777 でエラーは消えますが、作られたファイルの持ち主は UID 10001 のままで、ホストでは編集できませんでした。誰でも書き込めるフォルダーになるというセキュリティの問題もあります。UID を合わせるほうが、両方の問題を解決します。
named volume との違い
$ docker run --rm -v vol11:/app/data app11:1
uid=10001(app) gid=10001(app) groups=10001(app)
written
named volume(-v 名前:/パス)は、空のときにイメージの中身がコピーされ(Docker 公式ドキュメント)、確認環境では、Dockerfile で chown app:app したフォルダーにそのまま書き込めました。ホストのファイルを直接編集する必要が無い(DB のデータなど)なら、named volume にすると UID の問題が起きにくくなります。
| 種類 | 持ち主 | 向いている用途 |
|---|---|---|
bind mount(./data:/app/data) |
ホストのフォルダーの持ち主 | ソースコード、ホストで編集するファイル |
named volume(vol:/app/data) |
空のときイメージの中のフォルダーの持ち主 | DB のデータ、キャッシュ |
イメージに無いパスに named volume を付けたとき(-v vol12:/work)は、root の持ち主のフォルダーになり、touch: /work/a: Permission denied になりました。Dockerfile で、そのフォルダーを作って chown しておきます。
似たエラー:Read-only file system
$ docker run --rm -v "$PWD/data:/app/data:ro" --user "$(id -u):$(id -g)" app11:1
sh: can't create /app/data/out.txt: Read-only file system
:ro(読み取り専用)で付けると、UID が合っていても書けません。 Permission denied とは別の原因です。
確認の手順(チェックリスト)
| 順番 | 確認すること | 方法 |
|---|---|---|
| 1 | ホストの UID | id |
| 2 | フォルダーの持ち主 | ls -lnd フォルダー(-n で数字) |
| 3 | コンテナで動く UID | docker run ... id |
| 4 | 合わせる | --user "$(id -u):$(id -g)"、または Dockerfile の ARG UID |
| 5 | compose なら | user: "${UID}:${GID}" の値が空になっていないか(.env) |
| 6 | フォルダーが root の持ち主 | -v が自動で作った。先に mkdir |
| 7 | :ro が付いていないか |
Read-only file system なら読み取り専用 |
よくある質問
Q. Docker Desktop(Mac・Windows)でも同じですか。
A. 確認環境は Linux の Docker Engine です。Docker Desktop は、ホストとのファイル共有の仕組みが違うため、同じ結果にならないことがあります。同じ手順(id と ls -ln)で、コンテナから見た UID と持ち主を確かめます。
Q. SELinux の環境で Permission denied になります。
A. UID が合っていても、SELinux のラベルで拒否されることがあります。Docker 公式ドキュメントでは、-v の z・Z オプションで SELinux のラベルを設定できるとされています。確認環境(SELinux 無し)では試していないので、使う前に公式ドキュメントで違いを確かめます。
まとめ
- コンテナで動く UID と、ホストのフォルダーの持ち主の UID が違うために起きる
idとls -lnで数字を比べる--user "$(id -u):$(id -g)"か、Dockerfile で UID を合わせる- compose の
user: "${UID}:${GID}"は、変数が空だと root で動く。.envに書く -vはホストに無いフォルダーを root の持ち主で作る。先にmkdirchmod 777は、ホストで編集できないファイルが増える。UID を合わせる
参考・出典
確認日はいずれも 2026年10月11日です。
- Docker Docs「Bind mounts」:https://docs.docker.com/engine/storage/bind-mounts/
- Docker Docs「Volumes」:https://docs.docker.com/engine/storage/volumes/
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Docker bind mountのPermission deniedの直し方」
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。