اگر یک کانتینر Docker نمیتواند داخل Volume یا Bind Mount فایل بسازد، ویرایش کند یا حذف کند و خطاهایی مثل Permission denied، EACCES، Operation not permitted یا mkdir: cannot create directory میبینید، مشکل در بسیاری از مواقع از ناهماهنگی UID و GID بین کاربری که داخل کانتینر اجرا میشود و مالک فایلهای Mountشده است.
راهحل کوتاه: ابتدا با id UID/GID پردازش داخل کانتینر و با stat مالکیت مسیر روی Host را پیدا کنید. در Bind Mount معمولاً باید مالکیت مسیر Host با UID/GID کاربر کانتینر هماهنگ شود یا کانتینر با UID/GID مناسب اجرا شود. در Named Volume بهتر است به مسیر داخلی Docker روی Host دست نزنید و تغییر مالکیت را از داخل یک کانتینر انجام دهید. استفاده از chmod 777 راهحل اصولی نیست.
چرا Docker Volume با Permission مشکل پیدا میکند؟
در Linux مالکیت فایلها بر اساس نام کاربر ذخیره نمیشود؛ هسته سیستم با شناسههای عددی UID و GID کار میکند.
فرض کنید روی Host کاربر شما UID برابر 1000 دارد، اما برنامه داخل کانتینر با UID برابر 1001 اجرا میشود. اگر یک پوشه روی Host متعلق به UID 1000 باشد و فقط مالک اجازه نوشتن داشته باشد، پردازش داخل کانتینر با UID 1001 نمیتواند روی آن بنویسد.
این موضوع مخصوص نام کاربری نیست. ممکن است داخل کانتینر نام کاربر node باشد و روی Host نام کاربر ubuntu؛ اگر هر دو UID یکسان داشته باشند، از دید مجوزهای سنتی Linux مالکیت عددی هماهنگ است.
اول مشخص کنید Bind Mount دارید یا Named Volume
این تفاوت برای تعمیر Permission بسیار مهم است.
Bind Mount
در Bind Mount یک مسیر واقعی از Host مستقیماً داخل کانتینر نمایش داده میشود:
services:
app:
volumes:
- ./data:/app/data
در این مثال پوشه ./data روی Host همان دادهای است که کانتینر در /app/data میبیند.
Named Volume
در Named Volume مدیریت محل ذخیرهسازی بر عهده Docker است:
services:
app:
volumes:
- app_data:/app/data
volumes:
app_data:
برای Named Volume بهتر است مسیر داخلی Docker روی Host را مستقیم ویرایش نکنید. مدیریت فایلهای آن را از طریق کانتینر انجام دهید.
چطور نوع Mount را پیدا کنیم؟
نام کانتینر را پیدا کنید:
docker ps
بعد Mountها را بررسی کنید:
docker inspect CONTAINER_NAME
در بخش Mounts به مقدار Type نگاه کنید.
اگر مقدار زیر باشد:
"Type": "bind"
با Bind Mount طرف هستید. اگر مقدار volume باشد، Named Volume دارید.
مرحله اول: UID و GID کاربر داخل کانتینر را پیدا کنید
اول ببینید پردازش اصلی کانتینر با چه کاربری اجرا میشود:
docker exec CONTAINER_NAME id
نمونه خروجی:
uid=1001(app) gid=1001(app) groups=1001(app)
در این مثال UID و GID هر دو 1001 هستند.
برای دیدن User تنظیمشده در Config کانتینر نیز میتوانید اجرا کنید:
docker inspect -f '{{.Config.User}}' CONTAINER_NAME
اگر خروجی خالی باشد، معمولاً Image کاربر خاصی در Config تعیین نکرده و ممکن است پردازش بهصورت root اجرا شود؛ با این حال دستور id داخل کانتینر مرجع عملی بهتری برای بررسی وضعیت فعلی است.
مرحله دوم: UID/GID پوشه روی Host را ببینید
اگر Bind Mount شما مثلاً این است:
./data:/app/data
روی Host اجرا کنید:
stat -c '%u:%g %a %n' ./data
نمونه:
1000:1000 755 ./data
اگر کانتینر با 1001:1001 اجرا شود اما پوشه متعلق به 1000:1000 باشد، احتمال Permission Error وجود دارد.
مالکیت همان مسیر را داخل کانتینر هم بررسی کنید
docker exec CONTAINER_NAME sh -c 'id; stat -c "%u:%g %a %n" /app/data'
اگر Image شما sh یا ابزار stat ندارد، باید از ابزارهای موجود همان Image استفاده کنید.
یک تست ساده برای تشخیص Permission
از داخل کانتینر تلاش کنید فایلی ایجاد کنید:
docker exec CONTAINER_NAME sh -c 'touch /app/data/permission-test'
اگر نتیجه این باشد:
touch: /app/data/permission-test: Permission denied
مالکیت، Mode، SELinux یا Read-only بودن Mount را بررسی کنید.
روش اول برای Bind Mount: اصلاح مالکیت با chown
اگر برنامه داخل کانتینر باید با UID/GID برابر 1001:1001 اجرا شود و پوشه Host فقط برای همان برنامه استفاده میشود، میتوانید مالکیت مسیر Host را اصلاح کنید.
قبل از اجرا: مطمئن شوید مسیر را درست نوشتهاید. اجرای اشتباه chown -R روی مسیرهایی مثل /، /var یا یک پوشه اشتباه میتواند خسارت جدی ایجاد کند.
مثال:
sudo chown -R 1001:1001 ./data
بعد وضعیت را بررسی کنید:
stat -c '%u:%g %a %n' ./data
و دوباره تست نوشتن بگیرید:
docker exec CONTAINER_NAME sh -c 'touch /app/data/permission-test && rm /app/data/permission-test'
آیا همیشه باید chown -R بزنیم؟
خیر. اگر فقط خود پوشه اصلی مالکیت اشتباه دارد و فایلهای داخلی عمداً Ownerهای متفاوتی دارند، اجرای Recursive میتواند تنظیمات درست را خراب کند.
در چنین حالتی فقط خود دایرکتوری را تغییر دهید:
sudo chown 1001:1001 ./data
قبل از هر تغییر Recursive بهتر است چند فایل را بررسی کنید:
find ./data -maxdepth 2 -printf '%u:%g %m %p\n' | head -n 50
روش دوم: اجرای کانتینر با UID/GID مناسب
اگر نمیخواهید مالکیت فایلهای Host را تغییر دهید، در بعضی برنامهها میتوانید کانتینر را با همان UID/GID کاربر Host اجرا کنید.
UID و GID فعلی Host را پیدا کنید:
id -u
id -g
فرض کنیم خروجی هر دو 1000 است.
با docker run:
docker run --rm \
--user 1000:1000 \
-v "$(pwd)/data:/app/data" \
IMAGE_NAME
Docker اجازه میدهد User را بهصورت UID، نام کاربر یا ترکیب UID:GID تعیین کنید.
تنظیم UID/GID در Docker Compose
در Compose میتوان از گزینه user استفاده کرد:
services:
app:
image: your-image
user: "1000:1000"
volumes:
- ./data:/app/data
اما Hard-code کردن 1000:1000 برای همه سرورها همیشه مناسب نیست.
روش منعطفتر استفاده از متغیر است:
services:
app:
image: your-image
user: "${LOCAL_UID}:${LOCAL_GID}"
volumes:
- ./data:/app/data
فایل .env کنار Compose:
LOCAL_UID=1000
LOCAL_GID=1000
مقادیر واقعی را از این دستورات بردارید:
id -u
id -g
مشکل اجرای کانتینر با UID عددی چیست؟
اجرای برنامه با یک UID عددی همیشه کافی نیست. بعضی برنامهها انتظار دارند:
- کاربر در
/etc/passwdوجود داشته باشد؛ - Home Directory معتبر داشته باشد؛
- گروههای مکمل مشخصی داشته باشد؛
- فایلهایی در مسیرهای دیگری مثل
/var/libیا/var/cacheقابلنوشتن باشند.
بنابراین اگر با user: "1000:1000" برنامه بالا نمیآید، مستندات Image را بررسی کنید. بعضی Imageها برای UID/GID متغیر روش مخصوص خودشان را دارند.
راهحل پایدار در Dockerfile با USER
اگر Image متعلق به خودتان است، بهتر است کاربر برنامه را هنگام Build مشخص کنید.
نمونه برای Debian/Ubuntu-based Image:
FROM debian:stable-slim
ARG APP_UID=10001
ARG APP_GID=10001
RUN groupadd -g ${APP_GID} appgroup \
&& useradd -m -u ${APP_UID} -g ${APP_GID} appuser
WORKDIR /app
COPY --chown=${APP_UID}:${APP_GID} . /app
USER ${APP_UID}:${APP_GID}
CMD ["./app"]
در این مثال:
APP_UIDUID کاربر برنامه است.APP_GIDGID گروه برنامه است.COPY --chownمالکیت فایلهای کپیشده در Image را از ابتدا تنظیم میکند.USERپردازش اصلی را با کاربر غیر root اجرا میکند.
Dockerfile اجازه میدهد USER با نام یا UID/GID عددی تعیین شود. همچنین COPY --chown برای تعیین مالکیت فایلهای کپیشده در Image کاربرد دارد.
رفع Permission در Named Volume
در Named Volume، Docker محل ذخیرهسازی را مدیریت میکند. بهجای اینکه مستقیماً وارد مسیرهای داخلی Docker روی Host شوید، بهتر است Volume را به یک کانتینر موقت Mount کنید.
ابتدا Volumeها را ببینید:
docker volume ls
اطلاعات Volume:
docker volume inspect VOLUME_NAME
هشدار: اگر Volume متعلق به PostgreSQL، MySQL، Redis یا نرمافزار دیگری است، UID/GID موردنیاز را از همان Image مشخص کنید. استفاده کورکورانه از 1000:1000 ممکن است دیتابیس را از دسترسی به فایلهای خودش محروم کند.
اگر مطمئن شدهاید برنامه باید با UID/GID برابر 1001:1001 به کل Volume دسترسی داشته باشد، ابتدا سرویس Writer را متوقف کنید:
docker compose stop app
سپس یک کانتینر موقت روی Volume اجرا کنید:
docker run --rm \
-v VOLUME_NAME:/data \
alpine \
chown -R 1001:1001 /data
بعد سرویس را دوباره بالا بیاورید:
docker compose up -d app
و لاگ را بررسی کنید:
docker compose logs --tail=100 app
چرا بهتر است /var/lib/docker/volumes را مستقیم chown نکنیم؟
Named Volume بخشی از Storage مدیریتشده Docker است. ورود مستقیم به مسیر داخلی Docker و اجرای دستورات Recursive روی آن میتواند اشتباه پرریسکی باشد، مخصوصاً وقتی چند Volume و دیتابیس روی سرور دارید.
روش قابلکنترلتر این است که فقط Volume موردنظر را Mount کرده و تغییر را داخل یک کانتینر موقت انجام دهید.
قبل از تغییر Permission، Read-only بودن Mount را چک کنید
گاهی مشکل اصلاً UID/GID نیست و Volume بهصورت Read-only Mount شده است.
در Compose ممکن است چنین چیزی وجود داشته باشد:
volumes:
- ./data:/app/data:ro
یا در long syntax:
volumes:
- type: bind
source: ./data
target: /app/data
read_only: true
در این حالت حتی Permission صحیح هم اجازه نوشتن نمیدهد.
وضعیت Mount را بررسی کنید:
docker inspect CONTAINER_NAME
در بخش Mount موردنظر، اگر مقدار RW برابر false باشد، Mount فقطخواندنی است.
چرا chmod 777 راهحل مناسبی نیست؟
یکی از جوابهای رایج در اینترنت این است:
chmod -R 777 ./data
این دستور به همه کاربران اجازه Read، Write و Execute میدهد و معمولاً مشکل Permission را از نظر ظاهری پنهان میکند، اما علت اصلی یعنی ناهماهنگی Owner و Group را حل نمیکند.
برای مسیرهای دارای فایل تنظیمات، کلیدها، دیتابیس، Upload یا داده حساس، این روش میتواند امنیت را کاهش دهد.
بهتر است ابتدا Owner و Group را درست کنید و سپس حداقل Permission لازم را بدهید.
نمونه Permission منطقیتر
برای یک دایرکتوری داده که فقط Owner باید بنویسد:
sudo chown -R 1001:1001 ./data
sudo chmod -R u+rwX,go-rwx ./data
این فقط یک مثال است. Mode درست به نوع برنامه و نیاز دسترسی گروه بستگی دارد.
اگر Host و کانتینر باید هر دو با اعضای یک Group روی فایلها کار کنند، میتوانید بهجای World Writable بودن، از Group Permission استفاده کنید.
حل مشکل با Group مشترک
فرض کنید دایرکتوری روی Host متعلق به Group با GID برابر 2000 است. برنامه داخل کانتینر نیز باید عضو همین Group باشد.
در برخی سناریوها میتوان Group اضافی را هنگام اجرای کانتینر اضافه کرد:
docker run \
--group-add 2000 \
IMAGE_NAME
بعد Permission دایرکتوری را طوری تنظیم میکنید که Group امکان نوشتن داشته باشد.
این روش برای محیطهایی مناسب است که هم Host و هم Container باید به داده مشترک دسترسی داشته باشند.
حفظ Group روی فایلهای جدید با setgid
اگر چند پردازش با یک Group مشترک روی یک پوشه کار میکنند، میتوانید بیت setgid را روی دایرکتوری فعال کنید تا فایلها و زیرپوشههای جدید Group دایرکتوری والد را به ارث ببرند.
مثال:
sudo chgrp -R 2000 ./shared-data
sudo chmod -R g+rwX ./shared-data
sudo find ./shared-data -type d -exec chmod g+s {} \;
قبل از استفاده، مطمئن شوید GID انتخابشده متعلق به Group موردنظر است.
اگر فایلهای جدید Permission اشتباه میگیرند
گاهی Owner درست است اما فایلهای تازه با Mode نامناسب ساخته میشوند. در این شرایط باید umask برنامه یا EntryPoint را بررسی کنید.
داخل کانتینر:
docker exec CONTAINER_NAME sh -c 'umask'
برای مثال 0022 معمولاً باعث میشود فایل جدید برای Owner قابلنوشتن باشد اما Group و Others فقط Read داشته باشند.
برخی Imageها Environment Variable مخصوص تنظیم umask دارند؛ نام آن بین پروژهها متفاوت است و باید مستندات همان Image بررسی شود.
اگر UID/GID درست است اما Permission denied ادامه دارد
روی سیستمهایی که SELinux فعال دارند، فقط Unix Permission تعیینکننده نیست. Label اشتباه روی Bind Mount نیز میتواند مانع دسترسی کانتینر شود.
وضعیت SELinux:
getenforce
در سیستمهای SELinux، Docker برای Bind Mount گزینههای :z و :Z نیز دارد. برای مثال:
volumes:
- ./data:/app/data:Z
:z و :Z رفتار یکسانی ندارند؛ قبل از استفاده باید مشخص کنید مسیر فقط برای یک کانتینر است یا بین چند کانتینر Share میشود.
Docker Rootless و userns-remap را فراموش نکنید
اگر Docker را در Rootless Mode یا با userns-remap اجرا میکنید، نگاشت UID/GID بین کانتینر و Host متفاوت از Docker معمولی است.
در این حالت ممکن است UID صفر داخل کانتینر روی Host UID دیگری باشد. بنابراین دیدن root داخل کانتینر الزاماً به معنی UID صفر واقعی روی Host نیست.
برای بررسی Rootless بودن Docker:
docker info | grep -i rootless
برای بررسی User Namespace:
docker info | grep -i userns
اگر Rootless یا userns-remap فعال است، قبل از chown کردن Host Path باید Mapping واقعی UID/GID را مشخص کنید.
هشدار مخصوص Volume دیتابیسها
اگر Permission Error مربوط به دیتابیس است، Owner را از روی حدس تغییر ندهید.
برای مثال ممکن است PostgreSQL، MySQL یا MariaDB داخل Image رسمی با UID مخصوص خودش اجرا شود. اگر کل Volume را به 1000:1000 تغییر دهید، سرویس ممکن است دیگر بالا نیاید.
ابتدا UID/GID واقعی Image را مشخص کنید:
docker run --rm IMAGE_NAME id
اگر Image برای Start نیاز به Command خاص دارد، میتوانید EntryPoint را موقتاً تغییر دهید:
docker run --rm --entrypoint sh IMAGE_NAME -c 'id'
البته همه Imageها Shell ندارند.
سناریوی عملی Docker Compose
فرض کنید پروژه این Compose را دارد:
services:
app:
image: myapp:latest
user: "1001:1001"
volumes:
- ./uploads:/app/uploads
اما برنامه میگوید:
EACCES: permission denied, open '/app/uploads/image.jpg'
روی Host:
stat -c '%u:%g %a %n' ./uploads
خروجی:
1000:1000 755 ./uploads
پردازش با UID 1001 در دایرکتوری 755 متعلق به UID 1000 اجازه نوشتن ندارد.
اگر این پوشه فقط متعلق به همان برنامه است:
sudo chown -R 1001:1001 ./uploads
بعد:
docker compose restart app
docker compose logs --tail=100 app
سناریوی دوم: فایلهای پروژه نباید روی Host با Owner ناشناخته ساخته شوند
این حالت در محیط Development رایج است. کانتینر با root اجرا میشود و فایلهایی روی Bind Mount میسازد که روی Host متعلق به UID صفر هستند.
مثلاً بعد از اجرای ابزار Build:
ls -ln
میبینید فایلها Owner برابر صفر دارند:
-rw-r--r-- 1 0 0 1542 build.log
برای ابزارهایی که با UID کاربر معمولی سازگارند، میتوانید Compose را با UID/GID Host اجرا کنید:
services:
builder:
image: my-builder
user: "${LOCAL_UID}:${LOCAL_GID}"
volumes:
- .:/workspace
چکلیست عیبیابی Permission در Docker
- مشخص کنید Mount از نوع Bind است یا Named Volume.
- با
docker exec ... idUID/GID کاربر کانتینر را پیدا کنید. - با
statمالکیت مسیر Mount را بررسی کنید. - بررسی کنید Volume با
:roیاread_onlyMount نشده باشد. - اگر Bind Mount است، Owner/Group مسیر Host را با نیاز برنامه هماهنگ کنید.
- اگر Named Volume است، تغییر مالکیت را از طریق کانتینر انجام دهید.
- برای دیتابیسها UID/GID را از مستندات یا خود Image استخراج کنید.
- اگر Unix Permission درست است، SELinux را بررسی کنید.
- Rootless Docker و userns-remap را در صورت فعال بودن در نظر بگیرید.
- بعد از اصلاح، یک تست واقعی Read/Write انجام دهید.
تست نهایی بعد از رفع مشکل
داخل کانتینر:
docker exec CONTAINER_NAME sh -c '
id
touch /app/data/.permission-test
ls -ln /app/data/.permission-test
rm /app/data/.permission-test
'
اگر بدون خطا تمام شود، برنامه از نظر پایه توانایی نوشتن روی مسیر را دارد.
سپس لاگ برنامه را هم بررسی کنید:
docker logs --tail=100 CONTAINER_NAME
Rollback؛ اگر chown اشتباه اجرا شد چه کنیم؟
بهترین Rollback این است که قبل از تغییر، مالکیت قبلی را ثبت کرده باشید.
برای یک مسیر کوچک میتوانید قبل از تغییر خروجی را ذخیره کنید:
stat -c '%u:%g %a %n' ./data
در پروژههای مهم، قبل از chown -R از Volume یا دادهها Backup بگیرید.
اگر میدانید Owner قبلی مثلاً 1000:1000 بوده است:
sudo chown -R 1000:1000 ./data
اما اگر Ownerهای متنوعی داخل همان درخت وجود داشتهاند، یک chown -R ساده نمیتواند ساختار قبلی را دقیق بازسازی کند؛ به همین دلیل Backup قبل از تغییرهای گسترده اهمیت دارد.
اجرای Docker روی VPS لینوکسی
اگر برای اجرای Docker، Docker Compose، دیتابیس یا سرویسهای Self-hosted به سرور نیاز دارید، میتوانید پلنهای خرید سرور مجازی ایران ایرانیکاسرور را بررسی کنید. برای سرویسهای کانتینری، علاوه بر CPU و RAM، فضای دیسک، Backup و مدیریت صحیح Volumeها اهمیت زیادی دارد.
تماس با پشتیبانی ایرانیکاسرور: 021-91302467
مقاله مرتبط
اگر Permission مشکل اصلی شما نیست و Docker بهدلیل خطاهای Storage یا لایه overlay2 دچار مشکل شده، مقاله عیبیابی Storage و overlay2 در Docker میتواند مسیر بعدی بررسی باشد.
اشتباههای رایج در رفع Permission Docker
- استفاده فوری از
chmod -R 777؛ - اجرای
chown -R 1000:1000بدون دانستن UID واقعی برنامه؛ - تغییر Owner کل Volume دیتابیس؛
- نادیده گرفتن
:roدر Compose؛ - دستکاری مستقیم مسیرهای داخلی Named Volume روی Host؛
- فرض اینکه نام کاربر مهمتر از UID عددی است؛
- نادیده گرفتن SELinux؛
- نادیده گرفتن Rootless Docker و userns-remap؛
- اجرای کانتینر با root صرفاً برای مخفی کردن Permission Error؛
- اجرای Recursive chown بدون Backup یا ثبت مالکیت قبلی.
سؤالات متداول
UID و GID در Docker چه هستند؟
UID شناسه عددی کاربر و GID شناسه عددی گروه در Linux است. دسترسی فایلها بر اساس این شناسهها و Permissionها تعیین میشود؛ به همین دلیل اختلاف UID/GID بین Host و کانتینر میتواند روی Bind Mount مشکل ایجاد کند.
چطور UID کاربر داخل کانتینر را پیدا کنیم؟
با دستور docker exec CONTAINER_NAME id میتوانید UID، GID و گروههای کاربر فعلی داخل کانتینر را مشاهده کنید.
آیا chmod 777 برای Docker Volume مناسب است؟
معمولاً خیر. این کار دسترسی بسیار بازی ایجاد میکند و علت اصلی مشکل را رفع نمیکند. راه بهتر، تنظیم Owner، Group و حداقل Permission موردنیاز برنامه است.
برای Named Volume میتوان مستقیم داخل /var/lib/docker chown زد؟
روش توصیهشده این نیست. Named Volume توسط Docker مدیریت میشود و بهتر است Volume را به یک کانتینر موقت Mount کنید و عملیات لازم را روی همان Volume انجام دهید.
چطور Docker Compose را با UID و GID کاربر Host اجرا کنیم؟
میتوانید در سرویس Compose از user: "${LOCAL_UID}:${LOCAL_GID}" استفاده کنید و مقادیر واقعی را در فایل .env قرار دهید؛ البته برنامه داخل Image باید اجرای Numeric UID را پشتیبانی کند.
اگر کانتینر با root اجرا شود Permission Error حل میشود؟
گاهی موقتاً بله، اما اجرای سرویس با root فقط برای دور زدن مشکل Permission راهحل مناسبی نیست و سطح دسترسی کانتینر را افزایش میدهد. بهتر است UID/GID و مالکیت داده درست تنظیم شوند.
چرا با وجود UID/GID صحیح هنوز Permission denied میگیرم؟
Mount ممکن است Read-only باشد، SELinux دسترسی را مسدود کرده باشد، Mode فایل اجازه نوشتن ندهد یا Docker با Rootless/User Namespace اجرا شود. این موارد را جداگانه بررسی کنید.
آیا تغییر UID/GID برای دیتابیس Docker خطرناک است؟
بله، اگر بدون دانستن Owner موردانتظار Image انجام شود. PostgreSQL، MySQL و سرویسهای مشابه ممکن است UID/GID مشخصی برای دادههای خود داشته باشند. قبل از تغییر Recursive مالکیت، UID/GID واقعی Image و Backup داده را بررسی کنید.
جمعبندی: رفع Permission در Docker Volume با تغییر تصادفی Permission حل نمیشود. اول مشخص کنید با Bind Mount یا Named Volume طرف هستید، سپس UID/GID پردازش داخل کانتینر را با مالک فایلها مقایسه کنید. در Bind Mount معمولاً اصلاح Owner مسیر Host یا اجرای کانتینر با UID/GID مناسب راهحل است؛ در Named Volume بهتر است تغییر مالکیت از داخل یک کانتینر انجام شود. اگر این موارد درست هستند اما خطا باقی مانده، Read-only بودن Mount، SELinux و User Namespace را بررسی کنید.






دیدگاهها (۰)