PUID dan PGID Docker Compose: Mengapa File Jadi 911:911
PUID dan PGID bukan pengaturan Docker, melainkan konvensi entrypoint image linuxserver.io. Pahami penyebab file bind mount menjadi 911:911 dan cara memperbaikinya.
Apa sebenarnya PUID dan PGID
PUID dan PGID adalah dua variabel lingkungan yang dibaca oleh image container tertentu saat startup. Docker sendiri tidak pernah membacanya. Keduanya merupakan konvensi yang digunakan oleh image linuxserver.io dan beberapa image lainnya. Karena itu, image yang tidak dirancang untuk membacanya akan mengabaikannya tanpa pesan.
Di dalam image linuxserver.io terdapat pengguna bernama abc. Pengguna ini dibuat saat build dengan UID (ID pengguna) 911 dan GID (ID grup) 911. Container dimulai sebagai root, menjalankan skrip init, lalu salah satu skrip tersebut mengubah nomor ID pengguna itu sebelum proses lainnya berjalan:
groupmod -o -g "${PGID}" abc
usermod -o -u "${PUID}" abcFlag -o memungkinkan penggunaan ID yang sudah dipakai di tempat lain. Setelah itu, proses init menurunkan hak akses dan menjalankan aplikasi sebagai abc. Jadi, PUID=1000 tidak pernah sampai ke Docker. Variabel tersebut mengubah nomor ID pengguna di dalam container sebelum aplikasi dimulai. Akibatnya, setiap file yang ditulis aplikasi akan tersimpan di disk Anda dengan kepemilikan 1000. Jika PUID tidak ditetapkan, abc tetap menggunakan 911. Inilah sebabnya bind mount yang belum dikonfigurasi akan dipenuhi file yang dimiliki oleh 911:911.
Dapatkan dua nomor Anda dengan id
Jalankan perintah ini pada host sebagai pengguna yang memiliki direktori data:
iduid=1000(deploy) gid=1000(deploy) groups=1000(deploy),27(sudo),988(docker)uid adalah PUID Anda, sedangkan gid adalah PGID Anda. Untuk skrip, id -u dan id -g mencetak angka tanpa teks tambahan. Pada sebagian besar image VPS baru, akun manusia pertama menggunakan 1000:1000, tetapi jangan menganggap nilainya selalu demikian. Server yang dibangun ulang atau akun kedua yang ditambahkan kemudian dapat menggunakan 1001 atau nilai yang lebih tinggi. Nilai yang salah di sini menjadi penyebab seluruh masalah. Jika service Anda berjalan menggunakan akun service khusus, bukan pengguna login Anda sendiri, jalankan id thatuser dan gunakan nomor yang dihasilkan.
Mengapa file Anda muncul sebagai 911:911
ls -l menampilkan ID numerik, bukan nama, jika tidak ada akun pada host yang cocok dengan ID tersebut. Tidak ada apa pun pada server Anda yang memiliki UID 911, sehingga tidak ada nama yang dapat ditampilkan. Gunakan ls -ln agar angka selalu ditampilkan dan ambiguitas tersebut hilang:
ls -ln /srv/appdata/sonarrdrwxr-xr-x 2 911 911 4096 Aug 7 09:12 Backups
-rw-r--r-- 1 911 911 512 Aug 7 09:12 config.xmlOutput tersebut menunjukkan bahwa container berjalan dengan nilai bawaan. Konfirmasikan dari dalam container, bukan dengan menebak:
docker exec sonarr id abc
docker compose logs sonarr | head -n 25Inisialisasi linuxserver mencetak hasilnya dalam dua baris pada log startup:
User UID: 911
User GID: 911Jika baris tersebut menampilkan 911 setelah Anda menetapkan PUID=1000 dalam file Compose, variabel tersebut tidak pernah diteruskan ke container. Penyebab yang umum adalah Anda mengedit docker-compose.yml lalu menjalankan docker compose restart, yang menggunakan kembali container yang sudah ada beserta environment awalnya. Perubahan environment memerlukan docker compose up -d, yang membuat ulang container.
Mengapa Anda tidak dapat menghapus file yang ditulis container
Kernel membandingkan angka, bukan nama. Shell Anda berjalan sebagai UID 1000. File tersebut dimiliki oleh UID 911. Direktori yang menampungnya adalah drwxr-xr-x dan juga dimiliki oleh 911, sehingga group dan other hanya memiliki izin baca dan eksekusi, tanpa izin tulis. Untuk menghapus file, Anda memerlukan izin tulis pada direktorinya, bukan pada file tersebut. Karena itu, Anda dapat melihat pesan ini meskipun file itu sendiri tampak tidak bermasalah:
rm: cannot remove '/srv/appdata/sonarr/config.xml': Permission deniedContainer yang menulis file akan mengalami kendala yang sama dari sisi lain. Jika direktori host dimiliki oleh user Anda dengan mode 755 dan aplikasi berjalan sebagai 911, penulisan pertama gagal dengan Permission denied dan aplikasi melaporkannya menggunakan istilahnya sendiri. Pada aplikasi .NET seperti Sonarr atau Radarr, kondisi tersebut ditampilkan sebagai UnauthorizedAccessException: Access to the path '/data/downloads' is denied. String izin di depan file menunjukkan set izin mana dari ketiganya yang digunakan untuk menilai akses Anda. Memahami cara membaca drwxr-xr-x dengan benar akan mengubah error yang membingungkan itu menjadi masalah yang jelas.
Ini secara khusus merupakan masalah bind mount. Saat Docker membuat named volume kosong dan memasangnya pada path yang sudah ada di image, Docker menyalin isi path tersebut ke dalam volume, termasuk ownership dan bit izin. Dengan demikian, aplikasi menemukan direktori yang sudah dimilikinya. Bind mount tidak memperoleh perlakuan tersebut. Docker memasang direktori host persis seperti kondisinya. Perbedaan ini merupakan salah satu alasan praktis untuk memahami kapan bind mount lebih tepat daripada named volume dan kapan tidak.
Memperbaiki direktori yang sudah bermasalah
Menetapkan PUID dan PGID mengubah perilaku aplikasi mulai saat itu. Pengaturan tersebut tidak otomatis memperbaiki file yang sudah ada di disk. Hentikan stack, perbaiki ownership secara manual, lalu jalankan kembali:
docker compose down
sudo chown -R 1000:1000 /srv/appdata/sonarr
docker compose up -dGunakan sudo chown -R "$(id -u):$(id -g)" /srv/appdata/sonarr jika Anda tidak ingin mengetik angkanya. Lakukan ini saat container berhenti, karena aplikasi yang sedang menulis ketika chown rekursif dijalankan dapat menghasilkan struktur direktori yang hanya sebagian diperbaiki dan memicu rangkaian error kedua yang sulit dipahami.
Hal yang tidak dapat diperbaiki oleh PUID dan PGID
Berikut bagian yang sering menyebabkan masalah, meskipun semua langkah sebelumnya sudah dilakukan dengan benar. Skrip init linuxserver hanya menjalankan chown pada tiga path saat startup: /app, /config, dan /defaults. Mount media Anda tidak termasuk dalam daftar tersebut. /data, /downloads, dan /tv diteruskan ke aplikasi tanpa perubahan. Jadi, jika sisi host dari mount tersebut memiliki kepemilikan yang tidak dapat ditulisi oleh user di dalam container, container akan start dengan bersih, menampilkan UID yang benar pada banner-nya, lalu gagal saat import pertama.
Itu adalah perilaku yang benar. Menjalankan chown secara rekursif pada library media berukuran dua belas terabyte setiap kali container start akan menimbulkan masalah besar. Artinya, direktori media menjadi tanggung jawab Anda. Direktori tersebut juga merupakan mount tempat masalah permission benar-benar terjadi.
Tiga cara untuk mengendalikan pengguna dan kapan masing-masing digunakan
Variabel lingkungan PUID dan PGID
Cara ini hanya berfungsi pada image yang entrypoint-nya membaca variabel tersebut. Metode ini populer karena container tetap dijalankan sebagai root, melakukan penyiapan sendiri, memperbaiki /config, lalu menurunkan hak akses. Docker Mods dan skrip init khusus tetap dapat digunakan. Kekurangannya, Anda bergantung pada konvensi, bukan fitur platform, dan nama variabelnya tidak seragam di seluruh proyek.
Kunci user: di Compose
Cara ini merupakan fitur Docker yang sebenarnya dan berfungsi pada semua image, karena runtime container menerapkannya sebelum kode milik image dijalankan:
services:
sonarr:
image: lscr.io/linuxserver/sonarr:latest
user: "1000:1000"Proses tidak pernah berjalan sebagai root, bahkan sesaat pun tidak. Ini memberikan peningkatan keamanan yang nyata. Namun, cara ini juga memutus fungsi apa pun dalam entrypoint yang memerlukan root. Pada image linuxserver, proyek mendukung cara ini berdasarkan upaya yang wajar dan hanya untuk image yang telah diuji. Batasannya spesifik: PUID dan PGID tidak lagi berpengaruh, Docker Mods tidak akan berjalan, service khusus tidak akan berjalan, dan Anda bertanggung jawab atas permission pada setiap volume yang di-mount. Pola yang didokumentasikan memadukan flag tersebut dengan /run yang dapat ditulisi:
user: 1000:1000
tmpfs:
- /run:uid=1000,gid=1000,exec
security_opt:
- no-new-privileges=trueSatu efek kosmetik dapat mengejutkan pengguna. user: numerik tidak memiliki entri yang cocok di /etc/passwd milik container, sehingga tool di dalam container menampilkan whoami: cannot find name for user ID 1000. ID tersebut valid dan akses file tetap berfungsi normal. Hanya pencarian nama yang gagal.
Rootless Docker
Rootless Docker menjalankan daemon itu sendiri sebagai pengguna Anda yang tidak memiliki hak istimewa. Dengan demikian, tidak ada proses pada host yang berjalan sebagai root sebenarnya. Cara ini mengubah perhitungan ownership sepenuhnya. UID container 0 dipetakan ke UID host milik pengguna yang menjalankan rootless Docker. UID container n untuk setiap n bernilai 1 atau lebih dipetakan ke subuid + (n - 1), dengan subuid sebagai dasar rentang yang dialokasikan untuk Anda di /etc/subuid dan /etc/subgid. Docker mengharuskan setidaknya 65,536 subordinate ID di sana.
Baca kembali pemetaan tersebut karena membalik saran umum. Pada rootless Docker, container yang menulis sebagai root menghasilkan file yang dimiliki oleh Anda. Container yang menulis sebagai UID 1000 menghasilkan file yang dimiliki oleh subordinate ID di sekitar 100999, yang tidak dapat diakses oleh shell Anda. Jadi, nilai PUID yang benar pada daemon rootful menjadi nilai yang salah di sini. Kedua mekanisme ini menyelesaikan masalah yang sama pada lapisan yang berbeda. Menumpuk keduanya tanpa memeriksa hasilnya dapat membuat Anda memiliki direktori yang memerlukan sudo untuk dihapus. Jika Anda memilih rootless, uji ownership satu file yang ditulis pada server Anda sendiri sebelum memigrasikan suatu library ke dalamnya.
Untuk sebagian besar stack self-hosted pada satu VPS, PUID dan PGID pada daemon rootful merupakan pilihan praktis karena itulah konfigurasi yang digunakan dan didokumentasikan untuk image tersebut. Gunakan user: jika README image menyatakan bahwa image tersebut telah diuji untuk cara itu, atau jika Anda menjalankan image upstream resmi yang sama sekali tidak mendukung PUID. Workspace dokumen seperti instance AFFiNE self-hosted pada satu VPS termasuk kasus terakhir tersebut karena tidak satu pun containernya membaca PUID. Ownership direktori database dan file yang diunggah ditentukan oleh runtime, bukan oleh apa pun dalam blok environment.
Kasus media stack: satu grup yang digunakan bersama oleh semua container
arr media stack dengan Sonarr, Radarr, dan klien download adalah contoh ketika konsep ini diterapkan secara langsung. Klien download menulis file yang sudah selesai ke /data/downloads. Sonarr kemudian membuat hardlink atau memindahkan file tersebut ke /data/media. Agar hardlink berfungsi, kedua container harus memiliki akses tulis ke tree yang sama. Jika klien download berjalan sebagai 1000, sedangkan Sonarr berjalan sebagai 1001, salah satunya akan memiliki file yang hanya dapat dibaca oleh container lainnya.
Solusinya adalah membuat grup bersama yang digunakan setiap container dalam stack sebagai PGID-nya:
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 {} +2 di awal 2775 adalah bit setgid. Pada direktori, bit ini berarti setiap file dan subdirektori baru yang dibuat di dalamnya akan mewarisi grup media, bukan grup utama milik pembuatnya. Dengan demikian, konfigurasi tetap berlaku untuk download baru tanpa harus menjalankan ulang chown. Keluar lalu masuk kembali, atau jalankan newgrp media, sebelum memeriksa akses Anda sendiri. Grup yang ditambahkan dengan usermod -aG tidak akan muncul pada sesi shell yang sudah terbuka.
Di dalam container, groupmod -o -g 13000 abc mengubah nomor grup abc menjadi 13000. Dengan demikian, abc menulis menggunakan GID yang sama dengan grup media pada host. Setiap container dalam stack tetap menggunakan PUID masing-masing dan berbagi satu PGID tersebut.
Selanjutnya, tetapkan UMASK=002 pada setiap container linuxserver dalam stack. Langkah ini sering terlewat. Nilai default pada image tersebut adalah UMASK=022, yang menghapus bit write grup dari setiap file baru. Akibatnya, file dibuat sebagai 0644 dan konfigurasi berbagi yang baru saja Anda buat tidak berfungsi. 002 menghasilkan file 0664 dan direktori 0775, sehingga grup dapat menulis:
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-stoppedKedua nilai tersebut harus ditempatkan dalam file .env di samping file Compose. Dengan demikian, seluruh stack membaca satu definisi:
PUID=1000
PGID=13000Compose membaca file tersebut secara otomatis untuk substitusi bergaya ${PUID}. Mekanisme ini sama dengan yang digunakan untuk kredensial. Praktik menyimpan nilai di luar docker-compose.yml dan ke dalam file .env juga berlaku di sini. Perbedaannya, kedua angka tersebut bukan informasi rahasia.
Verifikasi konfigurasi secara menyeluruh, bukan hanya mengandalkannya. Tulis file dari dalam salah satu container, lalu baca file tersebut dari host:
docker exec sonarr touch /data/downloads/permtest
ls -ln /srv/media/downloads/permtestHasil yang benar menunjukkan PUID Anda sebagai pemilik, 13000 sebagai grup, dan -rw-rw-r-- sebagai mode. Jika grup terbaca sebagai 1000, bit setgid tidak ada pada direktori tersebut. Jika mode terbaca sebagai -rw-r--r--, variabel UMASK tidak diterapkan. Periksa apakah Anda membuat ulang container, bukan hanya me-restart-nya. Setelah selesai, hapus file pengujian dengan rm /srv/media/downloads/permtest.
Image yang menggunakan variabel tertentu
Image linuxserver.io menggunakan PUID, PGID, dan UMASK. Paperless-ngx menggunakan nama yang berbeda untuk konsep yang sama: USERMAP_UID dan USERMAP_GID, yang keduanya secara default bernilai 1000. Dokumentasinya mengarahkan Anda untuk membaca nilai tersebut dari id -u dan id -g. Server foto juga menunjukkan variasi yang sama: PhotoPrism memiliki pasangan PHOTOPRISM_UID dan PHOTOPRISM_GID sendiri, sedangkan Immich tidak menyediakan padanan dan menyerahkan pengguna container ke kunci user: milik Docker. Jadi, memilih antara PhotoPrism dan Immich juga menentukan mekanisme mana yang harus Anda kelola untuk pustaka terbesar di server. Banyak image upstream resmi, termasuk image database dan web server yang umum digunakan, menyediakan pengguna bawaan tetap dan mengharuskan Anda menggunakan user: atau membiarkannya. Hal yang sama berlaku untuk infrastruktur yang ditambahkan kemudian. Jadi, menempatkan Authentik di depan aplikasi untuk satu login berarti menjalankan image server, Postgres, dan Redis resmi yang sama sekali tidak membaca PUID. Kepemilikan volume untuk image tersebut ditentukan oleh runtime, bukan oleh entrypoint yang dapat Anda konfigurasi.
Periksa README setiap image sebelum menyalin blok environment antarproyek. Docker meneruskan setiap environment variable yang Anda tetapkan ke container mana pun, terlepas dari apakah ada komponen di dalamnya yang membacanya. PUID yang tidak digunakan oleh komponen apa pun tidak menghasilkan error, peringatan, atau efek. Container berjalan sebagai pengguna yang ditetapkan pada akhir Dockerfile-nya. Anda dapat mengetahui pengguna tersebut dari kepemilikan file yang ditulisnya.
FAQ
Mengapa file Docker saya dimiliki oleh 911:911?
911 adalah UID dan GID pengguna abc yang disertakan dalam image linuxserver.io. Jika nilai ini terlihat, container dijalankan tanpa PUID dan PGID, sehingga skrip init mempertahankan nilai default bawaan. ls -l menampilkan angka mentah karena tidak ada akun pada host Anda yang memiliki ID 911, sehingga tidak ada nama yang dapat ditampilkan. Tetapkan PUID dan PGID ke output id, buat ulang container dengan docker compose up -d, lalu perbaiki file yang sudah ada menggunakan sudo chown -R 1000:1000 pada direktori yang terdampak.
Apakah PUID dan PGID berfungsi pada setiap image Docker?
Tidak. Keduanya bukan fitur Docker dan Docker tidak pernah membacanya. Keduanya hanya berfungsi pada image yang entrypoint-nya sendiri membaca nilai tersebut, lalu memanggil usermod dan groupmod sebelum memulai aplikasi. Ini berlaku untuk keluarga linuxserver.io dan beberapa proyek yang meniru pola tersebut. Proyek lain menggunakan nama berbeda, seperti USERMAP_UID dan USERMAP_GID pada paperless-ngx. Pada image yang tidak membaca keduanya, variabel tersebut diterima dan diabaikan tanpa peringatan.
Sebaiknya saya menggunakan PUID dan PGID atau kunci user: di Docker Compose?
Gunakan PUID dan PGID jika image mendukungnya, karena entrypoint tetap berjalan sebagai root dalam waktu yang cukup untuk memperbaiki /config dan memulai servicenya sendiri dengan benar. Gunakan user: jika image tidak mendukung PUID, atau jika README image menyatakan bahwa image tersebut telah diuji untuk operasi non-root. Pada image linuxserver, menetapkan user: membuat PUID dan PGID tidak berfungsi, menghentikan Docker Mods dan service kustom agar tidak berjalan, serta membuat Anda bertanggung jawab atas izin setiap volume yang di-mount.
Sonarr memiliki PUID yang benar, tetapi tetap tidak dapat memindahkan file. Apa masalahnya?
Periksa tiga hal berikut secara berurutan. Pertama, mount media itu sendiri: init hanya menjalankan chown pada /app, /config, dan /defaults, sehingga /data atau /downloads tetap menggunakan kepemilikan yang ditetapkan pada host. Kedua, grup bersama: jika klien unduhan dan Sonarr berjalan dengan GID berbeda, keduanya tidak dapat mengubah file milik yang lain. Tetapkan PGID yang sama pada setiap container dalam stack. Ketiga, umask: default image UMASK=022 menulis file sebagai 0644 tanpa bit tulis untuk grup, sehingga grup bersama sama sekali tidak berguna. Tetapkan UMASK=002 dan atur bit setgid pada direktori menggunakan chmod 2775 agar file baru mewarisi grup tersebut.