SSD Nodes Learn 🎉 VPS $4.99/月〜
ガイド Matt Connor著者 Matt Connor ・更新日 2026-08-08

Docker ComposeのPUIDとPGIDとは?設定方法

PUIDとPGIDはDockerの設定ではなく、linuxserver.ioイメージのentrypointが使う慣例です。bind mountが911:911になる理由と、ホストのUID・GIDに合わせる修正方法を解説します。

PUID と PGID の実際の意味

PUID と PGID は、一部のコンテナイメージが起動時に読み取る環境変数です。Docker 自体はこれらを参照しません。これらは linuxserver.io のイメージや一部の他のイメージで使われている慣例です。そのため、これらを読み取るように作られていないイメージでは、設定しても無視されます。

linuxserver.io のイメージ内には、abc というユーザーがあります。このユーザーはビルド時に UID (user ID) 911、GID (group ID) 911 で作成されます。コンテナは root として起動し、初期化スクリプトを実行します。そのスクリプトの 1 つが、他の処理を行う前にこのユーザーの ID を変更します。

groupmod -o -g "${PGID}" abc
usermod -o -u "${PUID}" abc

-o フラグを指定すると、別の場所ですでに使用されている ID も指定できます。その後、初期化処理によって権限が降格され、アプリケーションは abc として実行されます。したがって、PUID=1000 が Docker に到達することはありません。この変数は、アプリケーションの起動前にコンテナ内のユーザー ID を変更します。その結果、アプリケーションが書き込むすべてのファイルは、ホスト上で 1000 が所有するファイルになります。PUID を未設定のままにすると、abc は 911 のままです。そのため、設定していない bind mount には 911:911 が所有するファイルが作成され続けます。

idで2つの番号を取得します

データディレクトリを所有するユーザーとして、ホスト上で次を実行します。

id
uid=1000(deploy) gid=1000(deploy) groups=1000(deploy),27(sudo),988(docker)

uidがPUID、gidがPGIDです。スクリプトでは、id -uid -gを実行すると番号だけが出力されます。新規構築したVPSイメージの多くでは、最初の一般ユーザーが1000:1000ですが、決めつけないでください。サーバーを再構築した場合や、後から2つ目のアカウントを追加した場合は、1001以上になることがあります。ここで番号を間違えると、それが問題の全体的な原因になります。サービスが自分のログインユーザーではなく専用のサービスアカウントで実行されている場合は、id thatuserを実行し、その結果から番号を取得します。

ファイルの所有者が911:911と表示される理由

ls -lは、そのIDに一致するホスト上のアカウントがない場合、名前ではなく数値IDを表示します。サーバー上にUID 911のアカウントがないため、表示する名前もありません。常に数値を表示して曖昧さをなくすには、ls -lnを使用します。

ls -ln /srv/appdata/sonarr
drwxr-xr-x 2 911 911 4096 Aug  7 09:12 Backups
-rw-r--r-- 1 911 911  512 Aug  7 09:12 config.xml

この出力は、コンテナが組み込みのデフォルト値で実行されたことを示しています。推測せず、コンテナ内から確認してください。

docker exec sonarr id abc
docker compose logs sonarr | head -n 25

linuxserverのinitは、起動ログに結果を2行で出力します。

User UID:    911
User GID:    911

PUID=1000をComposeファイルに設定した後もこれらの行が911と表示される場合、変数はコンテナに渡っていません。よくある原因は、docker-compose.ymlを編集した後にdocker compose restartを実行したことです。このコマンドは既存のコンテナを元の環境変数のまま再利用します。環境変数の変更にはdocker compose up -dが必要です。このコマンドでコンテナを再作成します。

コンテナが書き込んだファイルを削除できない理由

カーネルが比較するのは名前ではなく番号です。シェルは UID 1000 で実行されています。ファイルの所有者は UID 911 です。ファイルを格納しているディレクトリは drwxr-xr-x で、これも 911 が所有しています。そのため、group と other に付与されているのは読み取りと実行の権限だけで、書き込み権限はありません。ファイルを削除するにはファイル自体ではなく、そのディレクトリへの書き込み権限が必要です。そのため、ファイル自体に問題がないように見えても、次のエラーが発生します。

rm: cannot remove '/srv/appdata/sonarr/config.xml': Permission denied

書き込みを行うコンテナも、反対側から同じ制限を受けます。ホストのディレクトリが mode 755 でユーザー所有になっていて、アプリケーションが 911 で実行される場合、最初の書き込みは Permission denied で失敗します。アプリケーションは、そのエラーを独自のメッセージとして報告します。Sonarr や Radarr などの .NET アプリケーションでは、UnauthorizedAccessException: Access to the path '/data/downloads' is denied として表示されます。ファイル名の前に表示される権限文字列から、3 つの権限セットのうち実際にどれが適用されているかを確認できます。drwxr-xr-x を正しく読み取る方法を理解すると、このエラーの原因が明確になります。

これは特に bind mount で発生する問題です。Docker が空の名前付き volumeを作成し、イメージ内に存在するパスへマウントする場合、そのパスの内容を所有者、権限ビットとともに volume へコピーします。そのため、アプリケーションは最初から自分が所有するディレクトリを使用できます。bind mount にはこの処理がありません。Docker はホストのディレクトリを、その状態のままマウントします。この違いがあるため、bind mount が named volume より適する場合と適さない場合を理解しておくことが実務上重要です。

すでに不正になっているディレクトリを修正する

PUID と PGID の設定変更は、以後のアプリケーションの動作を変えるだけです。ディスク上にすでに存在するファイルを遡って修正することはありません。スタックを停止し、自分で所有者を修正してから、再度起動してください。

docker compose down
sudo chown -R 1000:1000 /srv/appdata/sonarr
docker compose up -d

数値を入力したくない場合は sudo chown -R "$(id -u):$(id -g)" /srv/appdata/sonarr を使用してください。コンテナを停止した状態で実行します。アプリケーションの書き込み中に再帰的な chown を実行すると、ディレクトリツリーが部分的にしか修正されず、さらに別のエラーが発生して原因が分かりにくくなる可能性があります。

PUID と PGID では解決できない問題

ここが、手順を正しく実施した場合でも問題になる部分です。linuxserver の init は、起動時に /app/config/defaults の3つのパスだけに対して chown を実行します。メディアのマウントはこの一覧に含まれません。/data/downloads/tv は変更されずにアプリケーションへ渡されます。そのため、これらのマウントのホスト側で、コンテナユーザーが書き込めない所有権になっていると、コンテナは正常に起動し、バナーに正しい UID を表示した後、最初のインポートで失敗します。

これは正しい動作です。コンテナを起動するたびに、12 TB のメディアライブラリ全体に対して再帰的な chown を実行すると、深刻な問題になります。そのため、メディアディレクトリの管理は利用者が行う必要があります。また、実際に権限の問題が発生するのは、これらのマウントです。

ユーザーを制御する3つの方法と、それぞれを適用する場面

PUID および PGID 環境変数

これは、entrypoint がそれらの変数を読み取るイメージでのみ機能します。コンテナは root として起動し、独自の初期設定を行い、/config を修正してから権限を降格するため、広く使われています。Docker Mods やカスタム初期化スクリプトも引き続き動作します。一方で、プラットフォームの機能ではなく慣例に依存することになります。また、変数名はプロジェクト間で標準化されていません。

Compose の user: キー

これは実際の Docker 機能であり、すべてのイメージで機能します。イメージ独自のコードが実行される前に、コンテナランタイムが適用するためです。

services:
  sonarr:
    image: lscr.io/linuxserver/sonarr:latest
    user: "1000:1000"

プロセスは一瞬も root として実行されません。これは実質的なセキュリティ上の向上です。ただし、entrypoint で root 権限を必要とする処理は動作しなくなります。linuxserver のイメージでは、プロジェクトがテスト済みのイメージに限り、合理的な範囲でこの機能をサポートしています。注意点は明確です。PUID と PGID は効果を持たなくなり、Docker Mods は実行されず、カスタムサービスも実行されません。また、マウントするすべてのボリュームの権限を自分で管理する必要があります。公式ドキュメントの構成では、このフラグと書き込み可能な /run を組み合わせます。

user: 1000:1000
tmpfs:
  - /run:uid=1000,gid=1000,exec
security_opt:
  - no-new-privileges=true

見た目上の副作用に驚くことがあります。数値の user: に対応するエントリがコンテナの /etc/passwd にないため、コンテナ内のツールは whoami: cannot find name for user ID 1000 と報告します。ID は有効であり、ファイルアクセスも通常どおり機能します。失敗するのは名前の検索だけです。

Rootless Docker

Rootless Docker では、デーモン自体が権限のないユーザーとして実行されるため、ホスト上で real root として実行される処理はありません。所有権の対応関係が完全に変わります。コンテナ内の UID 0 は、Rootless Docker を実行しているホストユーザーの UID に対応します。また、n が 1 以上の場合、コンテナ内の UID nsubuid + (n - 1) に対応します。subuid/etc/subuid/etc/subgid でユーザーに割り当てられた範囲の基点です。Docker では、そこに少なくとも 65,536 個の subordinate ID が必要です。

この対応関係をもう一度確認してください。通常の推奨事項が逆になるためです。Rootless Docker では、root として書き込むコンテナが作成したファイルは、ホスト上では自分が所有します。一方、UID 1000 として書き込むコンテナが作成したファイルは、100999 前後の subordinate ID が所有します。そのため、シェルからそのファイルを操作できません。rootful デーモンで正しい PUID 値は、ここでは誤った値になります。2つの仕組みは異なる層で同じ問題を解決します。確認せずに重ねて使うと、削除するために sudo が必要なディレクトリが作成されることがあります。Rootless に移行する場合は、ライブラリを移行する前に、自分のサーバーで書き込まれたファイル1つの所有権を確認してください。

単一の VPS で運用する一般的なセルフホスト構成では、rootful デーモン上で PUID と PGID を使う方法が現実的です。イメージがこの方法を前提に構築され、ドキュメントでも説明されているためです。user: を使うのは、イメージの README にテスト済みと記載されている場合、または PUID をまったくサポートしない公式の upstream イメージを使用する場合です。

メディアスタックの例: コンテナ間で共有する 1 つのグループ

Sonarr、Radarr、ダウンロードクライアントを含む arr メディアスタックでは、これが単なる理論ではなくなります。ダウンロードクライアントは、完了したファイルを /data/downloads に書き込みます。続いて Sonarr は、そのファイルを /data/media にハードリンクするか移動します。ハードリンクを機能させるには、2 つのコンテナが同じツリーへの書き込み権限を持つ必要があります。ダウンロードクライアントが 1000 で実行され、Sonarr が 1001 で実行されている場合、一方が所有するファイルをもう一方は読み取りしかできません。

この問題は、スタック内のすべてのコンテナが PGID として使用する共有グループを作成すると解決できます。

sudo groupadd -g 13000 media
sudo usermod -aG media deploy
sudo chown -R deploy:media /srv/media
sudo find /srv/media -type d -exec chmod 2775 {} +
sudo find /srv/media -type f -exec chmod 0664 {} +

2775 の先頭にある 2 は setgid ビットです。ディレクトリでは、その中に新しく作成されるすべてのファイルとサブディレクトリが、作成者自身のプライマリグループではなく media グループを継承することを意味します。そのため、新しいダウンロードのたびに chown を再実行する必要がありません。自分のアクセス権を確認する前に、ログアウトして再度ログインするか、newgrp media を実行してください。usermod -aG で追加したグループは、すでに開いているシェルセッションには反映されません。

コンテナ内では、groupmod -o -g 13000 abc により abc グループの番号を 13000 に変更します。これにより、abc はホストの media グループと同じ GID で書き込みます。スタック内の各コンテナは固有の PUID を維持し、同じ PGID を共有します。

次に、スタック内のすべての linuxserver コンテナで UMASK=002 を設定します。ここを見落とす人が多くいます。これらのイメージのデフォルトは UMASK=022 です。これは新しく作成されるすべてのファイルからグループの書き込みビットを取り除くため、ファイルは 0644 として作成され、設定した共有が機能しません。002 により 0664 ファイルと 0775 ディレクトリが作成され、グループが書き込めるようになります。

services:
  sonarr:
    image: lscr.io/linuxserver/sonarr:latest
    container_name: sonarr
    environment:
      - PUID=${PUID}
      - PGID=${PGID}
      - UMASK=002
      - TZ=Etc/UTC
    volumes:
      - /srv/appdata/sonarr:/config
      - /srv/media:/data
    restart: unless-stopped

この 2 つの値は Compose ファイルの隣にある .env ファイルに記述します。これにより、スタック全体で 1 つの定義を読み取れます。

PUID=1000
PGID=13000

Compose はこのファイルを自動的に読み込み、${PUID} 形式の置換に使用します。これは認証情報にも使用する仕組みです。値を docker-compose.yml から分離して .env ファイルに記述する運用もここで適用できます。ただし、この 2 つの数値は Secret ではありません。

設定をそのまま信頼せず、最初から最後まで確認してください。1 つのコンテナ内からファイルを書き込み、ホストから読み取ります。

docker exec sonarr touch /data/downloads/permtest
ls -ln /srv/media/downloads/permtest

正常な結果では、所有者が PUID、グループが 13000、モードが -rw-rw-r-- と表示されます。グループが 1000 と表示された場合、そのディレクトリに setgid ビットがありません。モードが -rw-r--r-- と表示された場合、UMASK 変数が反映されていません。コンテナを再起動するのではなく、再作成したことを確認してください。完了したら、rm /srv/media/downloads/permtest でテストファイルを削除します。

どのイメージがどの変数を使用するか

linuxserver.io のイメージでは、PUIDPGIDUMASK を使用します。Paperless-ngx では、同じ考え方に対して別の名前を使用します。USERMAP_UIDUSERMAP_GID で、どちらもデフォルト値は 1000 です。Paperless-ngx のドキュメントでは、これらの値を id -uid -g から読み取るよう説明されています。一般的なデータベースや Web サーバーのイメージを含む多くの公式 upstream イメージでは、組み込みの固定ユーザーを使用し、user: を指定するか、何も指定しないことを前提としています。

そのため、プロジェクト間で環境変数のブロックをコピーする前に、各イメージの README を確認してください。Docker は、設定した環境変数を、内部で読み取るかどうかに関係なく、すべてのコンテナへ渡します。何も参照しない PUID を設定しても、エラー、警告、効果は発生しません。コンテナは、その Dockerfile の最後で指定されたユーザーとして実行されます。実際にどのユーザーで動作したかは、コンテナが書き込んだファイルの所有者から確認できます。

FAQ

Docker ファイルが 911:911 の所有になっているのはなぜですか?

911 は、linuxserver.io イメージに組み込まれた abc ユーザーの UID と GID です。この表示になるのは、コンテナが PUIDPGID を設定せずに起動し、init スクリプトが組み込みのデフォルト値をそのまま使用したためです。ホスト上に ID 911 のアカウントがないため、ls -l には名前ではなく数値が表示されます。id の出力を PUIDPGID に設定し、docker compose up -d でコンテナを再作成します。その後、影響を受けたディレクトリに対して sudo chown -R 1000:1000 を実行し、既存のファイルを修正します。

PUID と PGID はすべての Docker イメージで機能しますか?

いいえ。これらは Docker の機能ではなく、Docker が読み取ることもありません。イメージ独自の entrypoint がこれらを読み取り、アプリケーションの起動前に usermodgroupmod を呼び出す場合にだけ機能します。該当するのは linuxserver.io 系のイメージと、この方式を採用した一部のプロジェクトです。プロジェクトによっては、paperless-ngx の USERMAP_UIDUSERMAP_GID のように別の名前を使用します。どちらも読み取らないイメージでは、変数は受け付けられますが、警告なしで無視されます。

Docker Compose では PUID と PGID、または user: キーのどちらを使用すべきですか?

イメージが対応している場合は PUIDPGID を使用します。entrypoint が引き続き root として動作し、/config の修正と独自サービスの正常な起動を行えるためです。イメージが PUID に対応していない場合、またはイメージの README に非 root での動作がテスト済みと記載されている場合は、user: を使用します。linuxserver のイメージで user: を設定すると、PUID と PGID は機能しなくなります。また、Docker Mods とカスタムサービスが実行されなくなり、すべてのマウントボリュームの権限を自分で管理する必要があります。

Sonarr に正しい PUID を設定しているのに、ファイルを移動できません。何が問題ですか?

次の 3 点を順番に確認します。まず、メディアのマウント自体です。init は /app/config/defaults だけに chown を実行するため、/data または /downloads はホスト上の所有権をそのまま保持します。次に、共有グループです。ダウンロードクライアントと Sonarr が異なる GID で動作していると、互いのファイルを変更できません。そのため、スタック内のすべてのコンテナに同じ PGID を設定します。最後に、umask です。イメージのデフォルト値 UMASK=022 では、グループ書き込みビットなしで 0644 としてファイルが作成されるため、共有グループが機能しません。UMASK=002 を設定し、chmod 2775 でディレクトリに setgid ビットを設定すると、新しいファイルがグループを継承します。