تشغّل حاويتك:
docker run -p 3000:3000 my-app
فيرفض Docker برسالة طويلة. أهم جزء فيها هو آخرها، لأن Docker يكتب رسالتين مختلفتين حسب من يحجز المنفذ:
| آخر الرسالة | من يحجز المنفذ 3000 | أين تبحث |
|---|---|---|
Bind for 0.0.0.0:3000 failed: port is already allocated | حاوية Docker أخرى تعمل الآن | docker ps |
failed to bind host port 0.0.0.0:3000/tcp: address already in use | برنامج على جهازك خارج Docker | lsof أو netstat |
الرسالتان كما ظهرتا حرفيًا على Docker Engine 29:
docker: Error response from daemon: failed to set up container networking:
driver failed programming external connectivity on endpoint adoring_mclaren (...):
Bind for 0.0.0.0:3000 failed: port is already allocated
docker: Error response from daemon: failed to set up container networking:
driver failed programming external connectivity on endpoint objective_tharp (...):
failed to bind host port 0.0.0.0:3000/tcp: address already in use
اسم الـ endpoint (adoring_mclaren) هو الاسم العشوائي لحاويتك الجديدة التي فشلت، وليس اسم الحاجز. لا تبحث عنه.
افهم الربط أولًا: host:container
-p 3000:3000
────┬ ───┬
│ └─ منفذ الحاوية: ما يستمع عليه تطبيقك بالداخل
└────── منفذ المضيف: ما يُفتح على جهازك، وهذا هو المتنازَع عليه
لكل حاوية شبكتها المعزولة، فعشر حاويات تستطيع كلها الاستماع على 3000 بالداخل دون أي تعارض. التعارض دائمًا على الرقم الأيسر، لأن جهازك فيه منفذ 3000 واحد فقط.
الحالة 1: port is already allocated — حاوية أخرى
اسأل Docker مباشرة من ينشر هذا المنفذ:
docker ps --filter publish=3000 --format "table {{.Names}}\t{{.Ports}}"
NAMES PORTS
old-app 0.0.0.0:3000->3000/tcp
ثم أوقفها، أو أوقفها واحذفها:
docker stop old-app
docker rm old-app # اختياري: الإيقاف وحده يكفي لتحرير المنفذ
الشكل الأكثر شيوعًا: مشروعا compose على نفس المنفذ
عندك مشروعان، وكلاهما فيه مثلًا قاعدة بيانات على 5432:5432. نسيت الأول يعمل وشغّلت الثاني، فيفشل الثاني بنفس الرسالة. اعرف أي مشروع يعمل:
docker compose ls
NAME STATUS CONFIG FILES
shop running(1) /home/me/shop/compose.yaml
ثم من مجلد ذلك المشروع:
docker compose down # يوقف ويحذف حاويات المشروع وشبكته
docker compose stop يكفي أيضًا لتحرير المنفذ؛ الفرق أن down يحذف الحاويات والشبكة بدل إبقائها متوقفة.
تصحيح لمعلومة منتشرة: الحاوية المتوقفة لا تحجز المنفذ
قد تقرأ أن حاوية متوقفة "تبقى حاجزة للمنفذ حتى تُحذف". جرّبناها: بعد docker stop old-app صارت حالتها Exited بعمود PORTS فارغ، واشتغلت حاوية جديدة على 3000 فورًا. إن رأيت الخطأ بعد إيقاف حاويتك، فالأرجح:
- حاوية أخرى تعمل (
docker ps --filter publish=3000يكشفها)، أو - حاويتك القديمة عادت للعمل وحدها لأنها أُنشئت بـ
--restart alwaysأوunless-stoppedثم أُعيد تشغيل Docker أو الجهاز. أوقف هذا السلوك بـ:
docker update --restart=no old-app
الحالة 2: address already in use — برنامج خارج Docker
docker ps لن يجد شيئًا هنا، لأن الحاجز ليس حاوية. ابحث في الجهاز نفسه:
# لينكس / ماك
sudo lsof -i :3000
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
python3 4939 me 3u IPv4 11864 0t0 TCP *:3000 (LISTEN)
على لينكس يعطيك sudo ss -ltnp 'sport = :3000' نفس المعلومة إن لم يكن lsof مثبّتًا. وعلى ويندوز (PowerShell):
netstat -ano | findstr :3000 # الـ PID في آخر عمود
tasklist | findstr <PID> # اسم البرنامج
غالبًا يكون خادم تطوير (npm run dev) نسيته في طرفية أخرى، أو قاعدة بيانات مثبّتة على الجهاز مباشرة (PostgreSQL على 5432 أو MySQL على 3306) وتحاول تشغيل نسخة Docker منها على نفس المنفذ. تفاصيل إيقاف البرنامج الحاجز على كل نظام في حل خطأ EADDRINUSE: المنفذ مستخدم.
ويندوز: منفذ "محجوز" ولا أحد يستخدمه
على Docker Desktop بويندوز قد تظهر رسالة تبدأ بـ Ports are not available وتنتهي بـ:
An attempt was made to access a socket in a way forbidden by its access permissions.
مع أن netstat لا يُظهر أي برنامج على المنفذ. السبب أن ويندوز نفسه يحجز نطاقات منافذ (تستخدمها خدمات مثل Hyper-V وWSL 2). اعرضها:
netsh interface ipv4 show excludedportrange protocol=tcp
إن كان منفذك داخل أحد النطاقات، اختر منفذ مضيف خارجها.
الحل الفوري: غيّر الرقم الأيسر
لا يهمك من الحاجز وتريد أن تكمل؟ غيّر منفذ المضيف فقط:
docker run -p 3001:3000 my-app
# التطبيق بالداخل ما زال على 3000، وأنت تفتحه على localhost:3001
وفي compose.yaml:
services:
web:
image: my-app
ports:
- "3001:3000" # المضيف:الحاوية
وما دمت تعدّل الربط، فكّر في 127.0.0.1:
docker run -p 127.0.0.1:5432:5432 postgres
-p 5432:5432 وحدها تعني 0.0.0.0، أي أن المنفذ مفتوح على كل واجهات الشبكة في جهازك وليس فقط عليك. لقاعدة بيانات تطوير لا يحتاجها أحد غيرك، الربط على 127.0.0.1 أأمن.
بعد الحل: "اسم الحاوية مستخدم"
إذا كنت تشغّل بـ --name، ستلاحظ أن إعادة نفس الأمر بعد تحرير المنفذ تفشل هكذا:
docker: Error response from daemon: Conflict. The container name "/web" is already in use by container "5fe0499c...".
You have to remove (or rename) that container to be able to reuse that name.
السبب أن docker run الفاشل أنشأ الحاوية قبل أن يفشل في ربط المنفذ، فبقيت بحالة Created:
docker ps -a --filter name=web --format "table {{.Names}}\t{{.Status}}"
# NAMES STATUS
# web Created
docker rm web # ثم أعد docker run
أو شغّل التجارب بـ --rm من البداية، فتُحذف الحاوية تلقائيًا عند إيقافها.
قائمة تحقّق
- اقرأ آخر الرسالة:
port is already allocatedيعني حاوية، وaddress already in useيعني برنامجًا على الجهاز. - حاوية؟
docker ps --filter publish=3000، ثمdocker stop. - مشروع compose آخر؟
docker compose ls، ثمdocker compose downمن مجلده. - برنامج على الجهاز؟
lsof -i :3000أوnetstat -ano | findstr :3000. - ويندوز ولا أحد على المنفذ؟
netsh interface ipv4 show excludedportrange protocol=tcp. - مستعجل؟ غيّر الرقم الأيسر:
-p 3001:3000. - "الاسم مستخدم" بعد الحل؟
docker rm <الاسم>.
لفهم الشبكات والمنافذ في Docker من الأساس، أكمل مسار Docker بالعربي. وإن كانت المشكلة مع منفذ أقل من 1024 على لينكس، فراجع حل Permission denied في لينكس.