Dockerボリュームマウント時のroot権限問題と解決策
Docker Composeで開発環境を構築したら、.venvやnode_modulesがroot所有になってホストから削除できない...そんな経験ありませんか?
原因はDockerコンテナがデフォルトでrootとして実行されるからです。今回は非rootユーザーで実行する方法を解説します。
KEY TAKEAWAYS
この記事でわかること
- ボリュームマウントでファイルがroot所有になる原因
- Python/Node.jsのDockerfileで非rootユーザーを設定する方法
- ビルド時間を短縮する最適化テクニック
用語の定義
TERM 01
UID / GID
Linuxでユーザーとグループを識別する数値ID。一般ユーザーは通常UID 1000から始まります。Dockerコンテナ内のUID/GIDとホストのUID/GIDが一致すると、ファイル所有権も一致します。
TERM 02
USER命令
Dockerfileでコンテナ実行時のユーザーを指定する命令です。USER 1000のように数値で指定するか、USER appuserのようにユーザー名で指定します。
TERM 03
ボリュームマウント
ホストのディレクトリをコンテナ内にマウントする機能です。./backend:/appのように指定すると、ホストのファイルがコンテナから読み書きできます。
TERM 04
COPY --chown
ファイルをコピーする際に所有者を指定するオプションです。COPY --chown=1000:1000 . .のように使い、非rootユーザー所有でファイルをコピーします。
Source: Understanding the Docker USER Instruction - Docker Blog
何が起きたのか
Docker Composeで開発環境を動かしていたら、こんなエラーに遭遇しました。
ホストから.venvを操作できない
コンテナでuv syncを実行すると.venvがroot所有で作成されます。ホスト側のUID 1000ユーザーからは書き込み権限がないため、削除も再作成もできません。
rm -rf .venv: Permission denieduv run manage.py: failed to remove directory- DBファイルも同様に書き込みエラー
$ ls -la backend/.venv/ drwxr-xr-x 4 root root 4096 ... $ uv run manage.py migrate error: failed to remove directory `.venv/lib`: Permission denied (os error 13) $ rm -rf .venv rm: '.venv/pyvenv.cfg' を削除できません: 許可がありません $ sqlite3 backend/db.sqlite3 [ERROR] Failed to register: attempt to write a readonly database
なぜroot所有になるのか
原因はDockerコンテナがデフォルトでroot(UID 0)として実行されることです。
権限問題が発生する流れ
- docker-compose.yamlでホストディレクトリをマウント
volumes: ["./backend:/app"] - Dockerfileで
USERを指定しない場合、コンテナはrootで実行
→ コンテナ内の全ての操作がroot(UID 0)として行われる - コンテナ内で
uv syncを実行 →.venvがroot所有で作成
→ Linuxのバインドマウントはホスト側にもそのまま反映 - ホストユーザー(UID 1000)からは書き込み権限がない
→ 削除も再作成もできない状態に
ポイント: Dockerはファイルのuid/gidマッピングを行いません。コンテナ内でroot(UID 0)が作成したファイルは、ホスト側でもUID 0として見えます。
問題のある設定(変更前)
USER命令がないため、全ての操作がrootで実行されます。uv syncで作成される.venvもroot所有になります。
FROM python:3.11-slim COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv WORKDIR /app COPY pyproject.toml . RUN uv sync --no-dev COPY . . EXPOSE 8000 CMD ["sh", "-c", "uv run --no-dev python manage.py migrate && uv run --no-dev python manage.py runserver 0.0.0.0:8000"]
Source: Permission denied on accessing host directory in Docker - Stack Overflow
解決策: 非rootユーザーでコンテナを実行
解決策はホストユーザーと同じUID/GIDのユーザーをコンテナ内に作成し、そのユーザーで実行することです。
Python (uv) のDockerfile
変更のポイント
ARG UID=1000: ホストユーザーと同じUIDを指定useradd -m: ホームディレクトリ付きでユーザー作成chown /app: 作業ディレクトリの所有者を変更USER ${UID}: 依存関係インストール前にユーザー切り替えCOPY --chown: ファイルを非rootユーザー所有でコピー
FROM python:3.11-slim
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
ARG UID=1000
ARG GID=1000
RUN groupadd -g ${GID} appuser 2>/dev/null || true && \
useradd -m -u ${UID} -g ${GID} appuser 2>/dev/null || \
useradd -m -u ${UID} appuser 2>/dev/null || true
WORKDIR /app
RUN chown ${UID}:${GID} /app
USER ${UID}
COPY --chown=${UID}:${GID} pyproject.toml .
RUN uv sync --no-dev
COPY --chown=${UID}:${GID} . .
EXPOSE 8000
CMD ["sh", "-c", "uv run --no-dev python manage.py migrate && uv run --no-dev python manage.py runserver 0.0.0.0:8000"]Node.js のDockerfile
node:22-slimの特徴
node:22-slimイメージには既にUID 1000のnodeユーザーが存在します。新しくユーザーを作成する必要はありません。
- 既存の
nodeユーザー(UID 1000)を使用 npm ciの前にUSER nodeで切り替えnode_modulesが最初からnode所有で作成される
FROM node:22-slim WORKDIR /app RUN chown node:node /app USER node COPY --chown=node:node package*.json ./ RUN npm ci COPY --chown=node:node . . EXPOSE 5173 CMD ["npm", "run", "dev", "--", "--host", "0.0.0.0"]
確認コマンド:docker run --rm node:22-slim id node → uid=1000(node) gid=1000(node) groups=1000(node)
なぜUID 1000なのか
Linuxでは最初に作成される一般ユーザーのUIDは通常1000です。コンテナ内のユーザーUIDとホストのUIDが一致すれば、ファイル所有権も一致します。USER 1000で実行すると、作成されるファイルもUID 1000所有になり、ホスト側のユーザーから操作できます。
Source: Understanding the Docker USER Instruction - Docker Blog
追加の問題: AWS認証情報が読めなくなる
非rootユーザーに切り替えた後、別の問題が発生することがあります。
AWSプロファイルが見つからない
原因
docker-compose.yamlでAWS認証情報を/root/.awsにマウントしていた場合、コンテナ内ユーザーがappuserになると参照されません。
- root前提のパス:
/root/.aws - appuserのホーム:
/home/appuser
Error: Internal server error: The config profile (default) could not be found
解決策
マウント先をappuserのホームディレクトリに変更します。HOME環境変数も明示的に設定しておくと安心です。
services: backend: volumes: - ~/.aws:/home/appuser/.aws:ro environment: - HOME=/home/appuser
ビルド時間の最適化
非rootユーザー対応でビルドが遅くなった?それはchown -Rの順番が原因かもしれません。
遅いパターン vs 速いパターン
| パターン | やり方 | chown対象 | ビルド時間 |
|---|---|---|---|
| 遅いパターン | uv sync → chown -R | .venv内の数万ファイル | +60秒以上 |
| 速いパターン | USER → uv sync | 空の/appディレクトリのみ | 一瞬 |
最適化のポイント
依存関係インストール前にUSERを切り替えることで、.venvやnode_modulesが最初から非rootユーザー所有で作成されます。後からchownする必要がありません。
応急処置: 既に権限が壊れている場合
今すぐ動かしたい場合の応急処置です。
root所有ファイルを修正
sudo chownでホストユーザー所有に戻します。その後、Dockerfileを修正して再ビルドすれば、今後は問題が発生しません。
- コンテナを停止
- 権限を修正(sudo必要)
- Dockerfile修正後に再ビルド
# コンテナを停止 docker compose down # 権限を修正 sudo chown -R $(whoami):$(whoami) backend/.venv backend/db.sqlite3 # Dockerfileを修正後、再ビルド docker compose build --no-cache
よくある質問
docker compose build --build-arg UID=$(id -u) --build-arg GID=$(id -g)でホストのUID/GIDを渡せます。チーム開発の場合は、全員のUIDを統一するか、エントリーポイントスクリプトで動的に対応する方法もあります。
検証環境
この記事の内容は以下の環境で検証しています。
OS: Ubuntu 24.04.3 LTS x86_64 Docker: 29.1.1 Docker Compose: v2.40.3


まだコメントはありません。最初のコメントを残しませんか?