1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Windows × Dockerで「コンテナ内アプリからファイルが書けない」時の切り分けメモ

1
Last updated at Posted at 2026-05-08

WindowsでDocker開発をしていると、アプリ自体は動いているのに、ファイル操作まわりで急に詰まることがあります。

たとえば、こういうやつです。

  • PHPからログファイルを書けない
  • アップロードファイルを保存できない
  • コンテナ内で作ったファイルがホスト側で編集しづらい
  • なぜかファイルの所有者が root になる
  • Dockerfileで chown したのに効かない
  • chmod したのに直らない
  • WSL / Windows / VSCode / Docker のどこから触ったかで挙動が変わる

特に Windows + WSL2 + Docker Desktop + VSCode の構成だと、見た目上は同じファイルを触っているつもりでも、実際には権限や所有者の見え方がズレることがあります。

Dockerの権限問題は、単なる permission denied ではなく、

ホスト側のファイル所有者
コンテナ内の実行ユーザー
bind mount / volume
Windows / WSL の境界
WebサーバーやPHPの実行ユーザー

あたりが全部絡みます。

この記事では、Docker環境でファイル操作できない時に見るべき場所を、原因別に整理します。


まず結論

Dockerの権限問題で最初に見るべきなのは、この4つです。

ls -ln
id
ps aux
docker inspect コンテナ名

ポイントは、ユーザー名ではなく UID/GIDを数字で見る ことです。

ls -ln

たとえばこう表示された場合、

drwxr-xr-x 5 1000 1000 4096 storage

この 1000:1000 が、実際に書き込むプロセスのUID/GIDと合っているかを見ます。

Dockerの権限問題は、最終的にはだいたいこの問いに戻ります。

このファイルの実体はどこにあるのか
誰が所有しているのか
どのプロセスが書き込もうとしているのか
そのプロセスはどのUID/GIDで動いているのか

よくあるパターンと対策

Dockerfileでchownしたのに効かない時

状況

Dockerfileでこうしているのに、

RUN chown -R www-data:www-data /var/www/html

実際にはPHPやApacheから書き込めない。

原因

docker-compose.yml で bind mount している可能性があります。

services:
  app:
    volumes:
      - ./src:/var/www/html

この場合、コンテナ内の /var/www/html は、イメージ内のディレクトリではなく、ホスト側の ./src です。

つまり、Dockerfile内で chown しても、コンテナ起動時にホスト側ディレクトリで上書きされます。

Dockerfile内で /var/www/html をchown
↓
コンテナ起動時に ./src を /var/www/html にmount
↓
実際の所有者はホスト側 ./src に依存

対策

bind mountしている場合は、ホスト側の所有者を確認します。

ls -ln ./src

コンテナ側の実行ユーザーも確認します。

docker exec -it app id
docker exec -it app ps aux

Dockerfileの chown ではなく、ホスト側のUID/GIDと、コンテナ内の実行ユーザーを揃える必要があります。


Windows側とWSL側で触る場所が混ざっている時

状況

同じプロジェクトを触っているはずなのに、操作する場所によって挙動が変わる。

たとえば、以下が混ざっている状態です。

WSL内のLinuxパス:
  /home/user/project

Windows側から見たWSLパス:
  \\wsl.localhost\Ubuntu\home\user\project

Dockerコンテナ内:
  /var/www/html

VSCode Remote WSL:
  Linux側として編集

Windows側VSCode:
  Windows側からUNCパスとして編集

原因

Windows、WSL、Docker Desktop、VSCodeの境界をまたいでいるためです。

見た目上は同じファイルでも、

Windows側から触ったファイル
WSL側から触ったファイル
コンテナ内プロセスが作ったファイル

で、所有者や権限の見え方がズレることがあります。

特に、Windows側のVSCodeから \\wsl$\\wsl.localhost 経由で開いていると、Gitやファイル権限の扱いが微妙にややこしくなることがあります。

対策

Docker開発では、基本的にプロジェクトをWSL側に置き、WSL内からVSCodeを開くのが安定しやすいです。

cd ~/project
code .

この形なら VSCode Remote WSL として開かれ、Linux側の権限体系で扱いやすくなります。

Windows × Docker で権限問題が多い場合、まずここを疑う価値があります。


コンテナ内で作ったファイルがroot所有になる時

状況

コンテナ内で生成されたファイルが、ホスト側で root 所有になってしまう。

ls -ln
-rw-r--r-- 1 0 0 1234 sample.log

原因

コンテナ内のプロセスが root でファイルを作っているためです。

bind mountしている場合、コンテナ内で作られたファイルはホスト側にもそのUID/GIDで現れます。

コンテナ内root UID 0
↓
bind mount先にファイル作成
↓
ホスト側でもUID 0所有に見える

Windows環境だとここがさらに分かりづらくなります。

ファイルはWindows側のエクスプローラーやVSCodeから見えていても、実際にはWSL上のLinuxファイルシステムにあり、LinuxのUID/GIDで管理されています。

対策

開発用コンテナでは、docker-compose.ymlで実行ユーザーを指定する方法があります。

services:
  app:
    user: "1000:1000"

.env にUID/GIDを置いておくと扱いやすいです。

UID=1000
GID=1000
services:
  app:
    user: "${UID}:${GID}"

ただし、user: を指定しても、ApacheやNginxなどのworker processが別ユーザーで動く場合があります。

そのため、最終的には必ずプロセスを確認します。

docker exec -it app ps aux

ユーザー名は同じなのに書き込めない時

状況

ホスト側にもコンテナ側にも userwww-data がいるのに、なぜか書き込めない。

原因

Linuxの権限は、ユーザー名ではなくUID/GIDで見ています。

たとえばホスト側ではこうだとします。

id
uid=1000(user) gid=1000(user)

一方、コンテナ内の www-data はこうかもしれません。

id www-data
uid=33(www-data) gid=33(www-data)

この場合、名前がそれっぽくても、UID/GIDが違うので別ユーザー扱いです。

対策

名前ではなく数字で確認します。

ls -ln

例:

drwxr-xr-x 5 1000 1000 4096 app

この 1000:1000 と、実際に書き込むプロセスのUID/GIDが合っているかを確認します。


PHPからログファイルが書けない時

状況

アプリは表示されるが、ログだけ書けない。

Permission denied
failed to open stream

原因

ログディレクトリの所有者と、PHPを実行しているユーザーが違う可能性があります。

たとえばログディレクトリがホスト側で 1000:1000 所有なのに、PHPがコンテナ内で www-data(33:33) として動いている場合です。

対策

まずログディレクトリを数字で確認します。

ls -ln storage/logs

PHPやApacheの実行ユーザーを確認します。

docker exec -it app ps aux

Apacheの場合は、以下も確認します。

docker exec -it app cat /etc/apache2/envvars
APACHE_RUN_USER=www-data
APACHE_RUN_GROUP=www-data

実行ユーザーとログディレクトリの所有者がズレているなら、どちらかを揃えます。

開発環境なら、一時的に以下で切り分けることはあります。

chmod -R 777 storage/logs

ただし、これは恒久対応ではなく、原因確認用に留めた方がよいです。


Apacheの実行ユーザーが想定と違う時

状況

docker-compose.ymlで user: を指定したのに、Apacheから書き込めない。

services:
  app:
    user: "1000:1000"

原因

Apacheは、起動プロセスとworker processでユーザーが違うことがあります。

たとえば、起動はrootでも、実際のリクエスト処理は www-data で動くことがあります。

master process: root
worker process: www-data

この場合、docker exec で入った時のユーザーだけ見ても不十分です。

対策

実際のApacheプロセスを確認します。

docker exec -it app ps aux

Apacheの環境変数も確認します。

docker exec -it app cat /etc/apache2/envvars

必要であれば、docker-compose.ymlでApacheの実行ユーザーを指定します。

services:
  app:
    environment:
      APACHE_RUN_USER: appuser
      APACHE_RUN_GROUP: appgroup

ただし、イメージによってはこれだけでは反映されない場合があります。

その場合は、実際に読まれているApache設定を確認する方が確実です。


Nginxのcacheや一時ディレクトリだけ書けない時

状況

Nginx自体は起動しているのに、cacheや一時ディレクトリで権限エラーになる。

permission denied
/var/cache/nginx
/client_temp
/proxy_temp

原因

Nginxのworker processの実行ユーザーと、cacheディレクトリの所有者が合っていない可能性があります。

Nginxでは、master processはroot、worker processはnginxユーザー、という構成もあります。

master process: root
worker process: nginx

rootでコンテナに入って確認して「書ける」と思っても、worker processからは書けないことがあります。

対策

worker processのユーザーを確認します。

docker exec -it nginx ps aux

対象ディレクトリの所有者を確認します。

docker exec -it nginx ls -ln /var/cache/nginx

worker processのUID/GIDと、ディレクトリの所有者またはグループ権限を合わせます。


named volumeとbind mountを混同している時

状況

ファイルがどこにあるのか分からない。
ホスト側を変更してもコンテナ内に反映されない。
または、コンテナ内にあるのにホスト側で見つからない。

原因

bind mountとnamed volumeの違いを混同している可能性があります。

bind mount:

volumes:
  - ./src:/var/www/html

これはホスト側の ./src をコンテナ内に見せています。

named volume:

volumes:
  - db_data:/var/lib/mysql

これはDockerが管理するvolumeをコンテナ内に見せています。

対策

mount状態を確認します。

docker inspect コンテナ名

Mounts に以下のように出ます。

Type: bind
Source: /home/user/project/src
Destination: /var/www/html

または、

Type: volume
Name: project_db_data
Destination: /var/lib/mysql

named volumeの実体を確認する場合は以下です。

docker volume ls
docker volume inspect project_db_data

Composeでは、volume名にプロジェクト名のprefixが付くことがあります。

db_data

と書いていても、実際には

project_db_data

のような名前になっていることがあります。


既存volumeがroot所有で残っている時

状況

docker-compose.ymlやDockerfileを直したのに、まだ権限エラーが出る。

原因

初回起動時にroot所有で作られたファイルやディレクトリが、volume内に残っている可能性があります。

Dockerfileやcompose設定を後から直しても、既存volumeの中身は自動では直りません。

初回起動時にrootで作成
↓
volume内にroot所有で残る
↓
あとからuserを変えても書けない

対策

bind mountなら、ホスト側で所有者を直します。

sudo chown -R 1000:1000 ./data

named volumeなら、一時コンテナで直す方法があります。

docker run --rm \
  -v project_data:/data \
  alpine \
  chown -R 1000:1000 /data

ただし、MySQLなどのDBデータディレクトリに対して雑に chown -R するのは危険です。

DB系は、対象ユーザーと公式イメージの仕様を確認してから作業した方がよいです。


MySQLコンテナが権限エラーで起動しない時

状況

MySQLコンテナが起動しない。
または、起動してすぐ落ちる。

ログに以下のような内容が出ることがあります。

Permission denied
Can't create/write to file

原因

/var/lib/mysql の所有者が、MySQLコンテナ内の mysql ユーザーと合っていない可能性があります。

bind mountしている場合は、ホスト側ディレクトリの所有者が重要です。

services:
  mysql:
    image: mysql:5.7
    volumes:
      - ./mysql_data:/var/lib/mysql

対策

まずログを確認します。

docker logs mysql

ホスト側の所有者を確認します。

ls -ln ./mysql_data

MySQLイメージ内の mysql ユーザーのUID/GIDを確認します。

docker run --rm mysql:5.7 id mysql

必要なら、ホスト側の所有者を合わせます。

sudo chown -R 999:999 ./mysql_data

ただし、既存DBに対して行う場合は、必ずバックアップを取ってから実行した方が安全です。


chmod 777で直るが、それでいいのか迷う時

状況

chmod -R 777 すると直る。

chmod -R 777 storage

原因

本当の原因は、UID/GIDや実行ユーザーのズレです。

777 はそれを解決しているのではなく、誰でも書けるようにして通しているだけです。

原因: 実行ユーザーと所有者がズレている
対処: chmod 777
結果: 書けるが、設計上のズレは残る

対策

開発環境での一時的な切り分けなら使うことはあります。

ただし、恒久対応としては以下の順で考えた方がよいです。

1. UID/GIDを揃える
2. 実行ユーザーを確認する
3. 書き込み先ディレクトリを限定する
4. group権限で775/664にする
5. 必要ならACLを使う
6. 777は最後の一時回避にする

確認コマンドまとめ

所有者を数字で見る

ls -ln

現在のユーザーを見る

id

コンテナ内のユーザーを見る

docker exec -it app id
docker exec -it app id www-data

実際のプロセスを見る

docker exec -it app ps aux

mount状態を見る

docker inspect app

volumeを見る

docker volume ls
docker volume inspect volume_name

Apacheの実行ユーザー設定を見る

docker exec -it app cat /etc/apache2/envvars

コンテナ起動プロセスの環境変数を見る

docker exec -it app sh -c "cat /proc/1/environ | tr '\0' '\n'"

まとめ

Windows × Docker、特に WSL2 + Docker Desktop + VSCode の構成では、Dockerの権限問題がかなり見えづらくなります。

表面的には、どれも同じように見えます。

Permission denied
書けない
消せない
編集できない
所有者がrootになる

しかし、原因はだいたい次のどれかです。

- bind mountでDockerfileのchownが効いていない
- ホスト側とコンテナ側のUID/GIDが違う
- Windows側とWSL側でファイルの触り方が混ざっている
- Apache/Nginx/PHP/MySQLの実行ユーザーが想定と違う
- named volumeとbind mountを混同している
- 既存volumeがroot所有で残っている

まず見るべきコマンドはこれです。

ls -ln
id
ps aux
docker inspect コンテナ名

Dockerの権限問題は、Docker特有の魔法というより、LinuxのUID/GID問題が bind mount や WSL によって見えづらくなっているだけです。

最終的には、常にこの問いに戻ります。

このファイルの実体はどこにあり、
誰が所有していて、
どのプロセスが、
どのUID/GIDで書き込もうとしているのか?

ここが見えるようになると、Dockerの permission denied はかなり落ち着いて切り分けできます。

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?