Docker Compose में PUID और PGID का सही उपयोग कैसे करें
PUID और PGID Docker की सेटिंग्स नहीं हैं बल्कि linuxserver.io images का एक कन्वेंशन है। जानें कि bind mount फाइलें 911:911 ओनरशिप के साथ क्यों आती हैं और इसे कैसे ठीक करें।
PUID और PGID वास्तव में क्या हैं
PUID और PGID दो environment variables हैं जिन्हें कुछ container images startup के समय पढ़ती हैं। Docker स्वयं इन्हें कभी नहीं देखता है। ये एक convention हैं, जिनका उपयोग linuxserver.io images और कुछ अन्य images द्वारा किया जाता है, इसलिए जो image इन्हें पढ़ने के लिए नहीं लिखी गई है, वह इन्हें चुपचाप अनदेखा कर देती है।
एक linuxserver.io image के अंदर abc नाम का एक user होता है, जिसे build के समय 911 UID (user ID) और 911 GID (group ID) के साथ बनाया जाता है। Container root के रूप में start होता है, अपनी init scripts चलाता है, और उन scripts में से एक, किसी भी अन्य प्रक्रिया से पहले उस user को renumber कर देती है:
groupmod -o -g "${PGID}" abc
usermod -o -u "${PUID}" abc-o flag ऐसे ID की अनुमति देता है जो पहले से ही कहीं और उपयोग में है। उसके बाद init privileges को कम कर देता है और application को abc के रूप में चलाता है। इसलिए PUID=1000 कभी Docker तक नहीं पहुँचता है। यह variable application शुरू होने से पहले container के अंदर एक user को renumber करता है, जिसका अर्थ है कि वह application जो भी file लिखती है, वह आपके disk पर 1000 के स्वामित्व (ownership) के साथ save होती है। यदि PUID को unset छोड़ दिया जाए, तो abc 911 ही रहता है, यही कारण है कि एक unconfigured bind mount उन files से भर जाता है जिनका स्वामित्व 911:911 के पास होता है।
id के साथ अपने दो numbers प्राप्त करें
इसे host पर उस user के रूप में चलाएं जिसके पास data directories का स्वामित्व है:
iduid=1000(deploy) gid=1000(deploy) groups=1000(deploy),27(sudo),988(docker)uid आपका PUID है और gid आपका PGID है। किसी script के लिए, id -u और id -g केवल numbers print करते हैं। अधिकांश नए VPS images पर पहला human account 1000:1000 होता है, लेकिन ऐसा मानकर न चलें। एक rebuilt server, या बाद में जोड़ा गया दूसरा account, 1001 या उससे अधिक का मान देता है, और यहाँ गलत number होने का अर्थ है पूरी समस्या का कारण। यदि आपकी services आपके स्वयं के login user के बजाय एक dedicated service account के अंतर्गत चलती हैं, तो id thatuser चलाएं और वहां से numbers लें।
आपकी फाइलें 911:911 के रूप में क्यों दिखाई देती हैं
ls -l जब किसी ID से कोई host account मेल नहीं खाता है, तो यह नाम के बजाय numeric ID प्रिंट करता है। आपके सर्वर पर कोई भी UID 911 नहीं है, इसलिए प्रिंट करने के लिए कोई नाम मौजूद नहीं है। हर बार नंबर देखने और अस्पष्टता को दूर करने के लिए ls -ln का उपयोग करें:
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.xmlवह आउटपुट बताता है कि container built-in defaults के साथ चला था। अनुमान लगाने के बजाय container के अंदर से इसकी पुष्टि करें:
docker exec sonarr id abc
docker compose logs sonarr | head -n 25linuxserver init अपने परिणाम को startup log में दो लाइनों के रूप में प्रिंट करता है:
User UID: 911
User GID: 911यदि आपके Compose file में PUID=1000 सेट करने के बाद भी वे लाइनें 911 दिखाती हैं, तो variable container तक नहीं पहुँचा है। इसका सामान्य कारण यह है कि आपने docker-compose.yml को एडिट किया और फिर docker compose restart चलाया, जो अपने मूल environment के साथ मौजूदा container का ही पुन: उपयोग करता है। Environment में बदलाव के लिए docker compose up -d की आवश्यकता होती है, जो container को फिर से बनाता है।
कंटेनर द्वारा लिखी गई फाइल को आप डिलीट क्यों नहीं कर सकते
कर्नेल नामों की तुलना नहीं करता, केवल संख्याओं की तुलना करता है। आपका शेल UID 1000 के रूप में चलता है। फाइल UID 911 की है। इसे रखने वाली डायरेक्टरी drwxr-xr-x है और वह भी 911 की है, इसलिए ग्रुप और अन्य को केवल रीड और एक्जीक्यूट की अनुमति है, राइट की नहीं। किसी फाइल को डिलीट करने के लिए उस फाइल पर नहीं, बल्कि उसकी डायरेक्टरी पर राइट परमिशन की आवश्यकता होती है, इसलिए फाइल देखने में भले ही सामान्य लगे, फिर भी आपको यह त्रुटि मिलती है:
rm: cannot remove '/srv/appdata/sonarr/config.xml': Permission deniedलिखने वाला कंटेनर दूसरी तरफ से इसी बाधा का सामना करता है। यदि होस्ट डायरेक्टरी आपके यूजर की है और उसका मोड 755 है, जबकि एप्लिकेशन 911 के रूप में चलता है, तो उसका पहला राइट ऑपरेशन Permission denied के साथ विफल हो जाता है और एप्लिकेशन इसे अपने शब्दों में रिपोर्ट करता है। Sonarr या Radarr जैसे .NET एप्लिकेशन में यह UnauthorizedAccessException: Access to the path '/data/downloads' is denied के रूप में दिखाई देता है। फाइल के सामने मौजूद परमिशन स्ट्रिंग आपको बताती है कि तीन परमिशन सेट में से किस आधार पर आपका मूल्यांकन किया जा रहा है, और drwxr-xr-x को सही ढंग से पढ़ना ही वह तरीका है जिससे यह त्रुटि रहस्यमय के बजाय स्पष्ट हो जाती है।
यह विशेष रूप से bind mount की समस्या है। जब Docker एक empty named 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 स्टार्टअप पर केवल तीन paths का chown करता है: /app, /config और /defaults। आपके media mounts इस सूची में नहीं हैं। /data, /downloads और /tv को बिना किसी बदलाव के application को सौंप दिया जाता है। इसलिए, यदि उन mounts के host side पर ऐसी ownership है जिसे container user access नहीं कर सकता, तो container सही ढंग से start हो जाएगा, अपने banner में सही UID दिखाएगा, लेकिन पहली बार import करने पर विफल हो जाएगा।
यह सही व्यवहार है। हर बार container start होने पर बारह terabyte की media library पर recursive chown चलाना एक आपदा होगी। इसका मतलब यह है कि media directories की जिम्मेदारी आपकी है, और यही वे mounts हैं जहाँ permissions वास्तव में गलत हो जाती हैं।
उपयोगकर्ता को नियंत्रित करने के तीन तरीके और उनका उपयोग कब करें
PUID और PGID environment variables
यह केवल उन images पर काम करता है जिनका entrypoint इन्हें पढ़ता है। यह लोकप्रिय है क्योंकि container अभी भी root के रूप में start होता है, अपना setup खुद करता है, /config को ठीक करता है, और उसके बाद ही privileges को कम करता है। Docker Mods और custom init scripts काम करते रहते हैं। इसकी कमी यह है कि आप एक convention पर भरोसा कर रहे हैं, न कि platform feature पर, और variable के नाम सभी projects में एक समान नहीं होते।
Compose में user: key
यह एक वास्तविक Docker feature है और हर image पर काम करता है, क्योंकि container runtime इसे image के अपने code के चलने से पहले लागू कर देता है:
services:
sonarr:
image: lscr.io/linuxserver/sonarr:latest
user: "1000:1000"process कभी भी root के रूप में नहीं चलती, एक पल के लिए भी नहीं, जो कि एक वास्तविक security लाभ है। यह entrypoint में मौजूद किसी भी ऐसी चीज़ को भी तोड़ देता है जिसे root की आवश्यकता थी। linuxserver images पर, project इसे 'reasonable endeavours' के आधार पर और केवल उन images के लिए support करता है जिनका उसने परीक्षण किया है। इसकी सीमाएं विशिष्ट हैं: PUID और PGID का कोई प्रभाव नहीं रहता, Docker Mods नहीं चलेंगे, custom services नहीं चलेंगी, और आप हर mounted volume पर permissions के लिए जिम्मेदार होंगे। उनका documented pattern इस flag को एक writable /run के साथ जोड़ता है:
user: 1000:1000
tmpfs:
- /run:uid=1000,gid=1000,exec
security_opt:
- no-new-privileges=trueएक cosmetic side effect लोगों को हैरान करता है। एक numeric user: का container के /etc/passwd में कोई मेल नहीं खाता, इसलिए अंदर के tools whoami: cannot find name for user ID 1000 रिपोर्ट करते हैं। ID मान्य है और file access सामान्य रूप से काम करता है। केवल name lookup विफल रहता है।
Rootless Docker
Rootless Docker daemon को ही आपके unprivileged user के रूप में चलाता है, इसलिए host पर कुछ भी वास्तविक root के रूप में नहीं चलता। यह ownership की गणना को पूरी तरह बदल देता है। Container UID 0, rootless Docker चलाने वाले user के host UID पर map होता है, और 1 या उससे अधिक के किसी भी n के लिए container UID n, subuid + (n - 1) पर map होता है, जहाँ subuid वह range है जो आपको /etc/subuid और /etc/subgid में आवंटित की गई है। Docker वहां कम से कम 65,536 subordinate IDs की अपेक्षा करता है।
उस mapping को फिर से पढ़ें, क्योंकि यह सामान्य सलाह को उलट देता है। Rootless Docker के तहत, root के रूप में लिखने वाला container ऐसी files बनाता है जिनका owner आप होते हैं। UID 1000 के रूप में लिखने वाला container ऐसी files बनाता है जिनका owner 100999 के आसपास की कोई subordinate ID होती है, जिसे आपका shell access नहीं कर सकता। इसलिए, जो PUID value rootful daemon पर सही है, वह यहाँ गलत है। ये दोनों mechanisms एक ही समस्या को अलग-अलग layers में हल करते हैं, और बिना जाँच किए उन्हें एक साथ इस्तेमाल करने से ही लोग ऐसी directory बना बैठते हैं जिसे हटाने के लिए उन्हें sudo की आवश्यकता होती है। यदि आप rootless का उपयोग करते हैं, तो किसी library को migrate करने से पहले अपने सर्वर पर एक लिखी गई file की ownership की जाँच करें।
एक ही VPS पर अधिकांश self-hosted stacks के लिए, rootful daemon पर PUID और PGID का उपयोग करना व्यावहारिक विकल्प है, क्योंकि images इसी के लिए बनाई और documented की गई हैं। user: का उपयोग तब करें जब image README में लिखा हो कि वह इसके लिए tested है, या जब आप कोई ऐसी official upstream image चला रहे हों जिसमें PUID support बिल्कुल न हो।
मीडिया स्टैक का मामला: कंटेनरों के बीच साझा किया गया एक ग्रुप
एक Sonarr, Radarr और डाउनलोड क्लाइंट वाला arr मीडिया स्टैक वह जगह है जहाँ यह केवल सिद्धांत नहीं रह जाता। डाउनलोड क्लाइंट एक पूरी हो चुकी फाइल को /data/downloads में लिखता है। इसके बाद Sonarr उस फाइल को /data/media में हार्डलिंक या मूव करता है। हार्डलिंक के काम करने के लिए दोनों कंटेनरों को एक ही डायरेक्टरी ट्री पर राइट एक्सेस की आवश्यकता होती है। यदि डाउनलोड क्लाइंट 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 उसी GID के साथ फाइलें लिखे जो आपके होस्ट media ग्रुप का है। स्टैक का प्रत्येक कंटेनर अपना 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ये दोनों मान Compose फाइल के बगल में एक .env फाइल में होने चाहिए, ताकि पूरा स्टैक एक ही परिभाषा को पढ़े:
PUID=1000
PGID=13000Compose ${PUID} स्टाइल प्रतिस्थापन के लिए उस फाइल को स्वचालित रूप से पढ़ता है, जो वही तंत्र है जिसका उपयोग आप क्रेडेंशियल्स के लिए करते हैं। docker-compose.yml से मानों को बाहर रखने और उन्हें .env फाइल में रखने की आदतें यहाँ भी लागू होती हैं, बस अंतर यह है कि ये दो नंबर गुप्त नहीं हैं।
कॉन्फ़िगरेशन पर भरोसा करने के बजाय इसे एंड-टू-एंड सत्यापित करें। एक कंटेनर के अंदर से एक फाइल लिखें और उसे होस्ट से पढ़ें:
docker exec sonarr touch /data/downloads/permtest
ls -ln /srv/media/downloads/permtestएक सही परिणाम में आपका PUID ओनर के रूप में, 13000 ग्रुप के रूप में, और -rw-rw-r-- मोड के रूप में दिखना चाहिए। यदि ग्रुप 1000 दिखाता है, तो उस डायरेक्टरी से setgid बिट गायब है। यदि मोड -rw-r--r-- दिखाता है, तो UMASK वेरिएबल प्रभावी नहीं हुआ है। ऐसे में जांचें कि क्या आपने कंटेनर को रीस्टार्ट करने के बजाय फिर से बनाया (recreate) है। काम पूरा होने पर rm /srv/media/downloads/permtest के साथ टेस्ट फाइल को हटा दें।
कौन सी images किन variables का उपयोग करती हैं
linuxserver.io images PUID, PGID और UMASK का उपयोग करती हैं। Paperless-ngx इसी विचार के लिए अलग नामों का उपयोग करता है: USERMAP_UID और USERMAP_GID, जो दोनों डिफ़ॉल्ट रूप से 1000 पर सेट होते हैं, और इसका documentation आपको इन्हें id -u और id -g से पढ़ने के लिए कहता है। कई आधिकारिक upstream images, जिनमें सामान्य database और web server images शामिल हैं, एक निश्चित built-in user के साथ आती हैं और आपसे अपेक्षा करती हैं कि आप user: का उपयोग करें या उसे वैसा ही रहने दें।
इसलिए, किसी project के बीच environment block को copy करने से पहले प्रत्येक image का README देखें। Docker आपके द्वारा सेट किए गए किसी भी environment variable को container में भेज देता है, चाहे उसके अंदर कोई उसे पढ़े या न पढ़े, और एक ऐसा PUID जिसे कोई भी process consume नहीं करती, वह न तो कोई error देता है, न ही कोई warning और न ही उसका कोई प्रभाव पड़ता है। Container उसी user के रूप में चलता है जिस पर उसकी अपनी Dockerfile समाप्त हुई थी, और इसका पता आपको उसके द्वारा लिखी गई files के ownership से चलता है।
FAQ
मेरे Docker files का owner 911:911 क्यों है?
911, linuxserver.io images में इनबिल्ट abc user का UID और GID है। इसे देखने का मतलब है कि container बिना PUID और PGID सेट किए शुरू हुआ, इसलिए इसके init script ने इनबिल्ट defaults को ही रहने दिया। ls -l raw numbers दिखाता है क्योंकि आपके host पर किसी भी account की ID 911 नहीं है, इसलिए दिखाने के लिए कोई नाम मौजूद नहीं है। PUID और PGID को id के output पर सेट करें, docker compose up -d के साथ container को recreate करें, और फिर प्रभावित directory पर sudo chown -R 1000:1000 चलाकर existing files को ठीक करें।
क्या PUID और PGID हर Docker image पर काम करते हैं?
नहीं। ये Docker का feature नहीं हैं और Docker इन्हें कभी नहीं पढ़ता। ये केवल उन images पर काम करते हैं जिनका entrypoint इन्हें पढ़ता है और application शुरू करने से पहले usermod और groupmod को कॉल करता है। यह linuxserver.io family और इस pattern को कॉपी करने वाले कुछ projects में होता है। अन्य projects अलग names का उपयोग करते हैं, जैसे paperless-ngx में USERMAP_UID और USERMAP_GID। ऐसी image पर जो इनमें से किसी को नहीं पढ़ती, variables स्वीकार तो कर लिए जाते हैं लेकिन बिना किसी चेतावनी के ignore कर दिए जाते हैं।
क्या मुझे PUID और PGID का उपयोग करना चाहिए या Docker Compose में user: key का?
जब image इनका समर्थन करती हो तो PUID और PGID का उपयोग करें, क्योंकि entrypoint /config को ठीक करने और अपनी services को सही ढंग से शुरू करने के लिए पर्याप्त समय तक root के रूप में चलता है। जब image में PUID support न हो, या image README में यह लिखा हो कि इसे non-root operation के लिए test किया गया है, तब user: का उपयोग करें। linuxserver image पर, user: सेट करने से PUID और PGID निष्क्रिय हो जाते हैं, Docker Mods और custom services चलना बंद हो जाती हैं, और हर mounted volume की permissions की जिम्मेदारी आपकी हो जाती है।
Sonarr के पास सही PUID है लेकिन फिर भी वह files move नहीं कर पा रहा है। क्या समस्या है?
क्रमवार तीन चीजों की जाँच करें। पहला, media mount स्वयं: init केवल /app, /config और /defaults को chown करता है, इसलिए /data या /downloads पर host वाली ownership ही बनी रहती है। दूसरा, shared group: यदि download client और Sonarr अलग-अलग GID के तहत चलते हैं, तो कोई भी दूसरे की files को modify नहीं कर सकता, इसलिए stack के हर container को एक ही PGID दें। तीसरा, umask: image का default UMASK=022 files को 0644 के रूप में लिखता है जिसमें group write bit नहीं होता, जो shared group के उद्देश्य को पूरी तरह विफल कर देता है। UMASK=002 सेट करें और chmod 2775 के साथ directories पर setgid bit सेट करें ताकि नई files group को inherit कर सकें।