SSD Nodes Learn 🎉 VPS $4.99/माह से
गाइड Matt Connorलेखक: Matt Connor · अपडेट किया गया: 2026-08-07

Docker Compose में command और entrypoint का सही उपयोग

Docker Compose में ENTRYPOINT मुख्य प्रोग्राम है और command उसके तर्क हैं। जानें कि कैसे ENTRYPOINT सेट करने से CMD हट जाता है और सभी चार ओवरराइड संयोजनों का सही तरीका क्या है।

Docker Compose command और entrypoint, एक नियम में

Docker Compose में, entrypoint: उस प्रोग्राम को सेट करता है जो चलता है और command: उस प्रोग्राम को दिए जाने वाले arguments को सेट करता है। कंटेनर की process, entrypoint सूची होती है जिसके अंत में command सूची जुड़ी होती है। इस पेज पर बाकी सभी व्यवहार इसी एक वाक्य से तय होते हैं।

ये दो keys दो Dockerfile निर्देशों पर मैप होती हैं। entrypoint: इमेज के ENTRYPOINT को बदल देता है। command: इमेज के CMD को बदल देता है। ये स्वतंत्र नहीं हैं, और यहीं लोग उलझ जाते हैं: entrypoint: सेट करने से इमेज का CMD भी हट जाता है। Compose specification इसे सीधे तौर पर बताती है। यदि entrypoint non-null है, तो Compose इमेज से किसी भी default command को अनदेखा कर देता है।

इमेज में पहले से घोषित निर्देशों को पढ़ें

किसी भी चीज़ को ओवरराइड करने से पहले, यह देखें कि इमेज क्या प्रदान करती है।

docker image inspect --format '{{json .Config.Entrypoint}}' postgres:16
docker image inspect --format '{{json .Config.Cmd}}' postgres:16

आपको ["docker-entrypoint.sh"] और ["postgres"] मिलते हैं, इसलिए कंटेनर docker-entrypoint.sh postgres चलाता है। वह स्क्रिप्ट पहली बार बूट होने पर डेटा डायरेक्टरी बनाती है, POSTGRES_* वेरिएबल्स को पढ़ती है, प्रिविलेज को postgres यूजर पर ड्रॉप करती है, और अंत में दिए गए आर्गुमेंट्स को exec करती है। आप किस हिस्से को बदलना चाहते हैं, यह जानना ही मुख्य निर्णय है। डेटाबेस को फ्लैग पास करने के लिए आप command: को रिप्लेस करते हैं। यदि आप entrypoint: को रिप्लेस करते हैं, तो वह सेटअप बिल्कुल भी नहीं चलेगा।

चार संयोजन, एक छोटी छवि पर दिखाए गए हैं

एक ऐसी image बनाएँ जिसका एकमात्र कार्य उन argument list को प्रिंट करना है जिसके साथ उसे शुरू किया गया था।

FROM alpine:3.20
ENTRYPOINT ["/bin/echo", "ep"]
CMD ["cmd"]
docker build -t argdemo .
services:
  demo:
    image: argdemo

प्रत्येक edit के बाद docker compose up चलाएँ और इसके द्वारा log की गई एकल पंक्ति को पढ़ें।

  • कोई भी key set नहीं है। प्रक्रिया /bin/echo ep cmd है और log में ep cmd दिखाई देता है।
  • केवल command: ["cmd2"] प्रक्रिया /bin/echo ep cmd2 है। entrypoint अपरिवर्तित रहता है और केवल arguments बदलते हैं।
  • केवल entrypoint: ["/bin/echo", "ep2"] प्रक्रिया /bin/echo ep2 है और log में ep2 दिखाई देता है। image से cmd हट गया है, और कोई चेतावनी नहीं मिलती है।
  • दोनों keys set हैं। प्रक्रिया /bin/echo ep2 cmd2 है। यह एकमात्र स्थिति है जहाँ आप पूरी argument list को नियंत्रित करते हैं।

Entrypoint सेट करने पर image CMD क्यों हट जाता है

किसी image का CMD उस image के ENTRYPOINT के लिए डिफ़ॉल्ट तर्क सूची (argument list) के रूप में लिखा जाता है। यदि आप entrypoint को बदलते हैं, तो वे तर्क अब एक ऐसे प्रोग्राम से संबंधित हो जाते हैं जो चल ही नहीं रहा है। इसलिए, Compose उन्हें हटा देता है, बजाय इसके कि वह ऐसी कमांड लाइन बनाए जिसे image बनाने वाले ने कभी नहीं चाहा था। docker run --entrypoint भी इसी तरह व्यवहार करता है, इसलिए यह Docker का व्यवहार है, न कि Compose की कोई कमी।

इसका परिणाम स्पष्ट है। nginx:1.27 में ENTRYPOINT ["/docker-entrypoint.sh"] और CMD ["nginx", "-g", "daemon off;"] घोषित होते हैं। यदि आप entrypoint: /custom-init.sh सेट करते हैं, तो आपकी स्क्रिप्ट खाली तर्क सूची के साथ शुरू होती है। ऐसी स्क्रिप्ट जो सामान्यतः exec "$@" पर समाप्त होती है, उसके पास exec करने के लिए कुछ नहीं बचता। इसलिए exec कुछ नहीं करता, स्क्रिप्ट अपनी अंतिम पंक्ति तक पहुँच जाती है, और container बिना किसी त्रुटि संदेश के कोड 0 के साथ बंद हो जाता है। तर्कों को स्वयं वापस जोड़ें:

services:
  web:
    image: nginx:1.27
    entrypoint: /custom-init.sh
    command: ["nginx", "-g", "daemon off;"]

याद रखने योग्य नियम: जब भी आप entrypoint: सेट करें, उसी संपादन में यह तय करें कि command: क्या होना चाहिए।

Exec form और shell form, और Compose में इनका अंतर

Dockerfile दो तरह के सिंटैक्स स्वीकार करती है। CMD ["nginx", "-g", "daemon off;"] exec form है: इसमें बाइनरी सीधे चलती है, और कोई शेल शामिल नहीं होता। CMD nginx -g "daemon off;" shell form है: Docker इसे /bin/sh -c 'nginx -g "daemon off;"' के रूप में फिर से लिखता है, जिससे पहले एक शेल चलता है और आपका प्रोग्राम उसका चाइल्ड प्रोसेस बन जाता है।

Compose इस नियम का पालन नहीं करता है, और यह बात लोगों को हैरान करती है। command: में दी गई स्ट्रिंग को सीधे आर्गुमेंट्स में विभाजित करके निष्पादित (execute) किया जाता है, इसमें कोई /bin/sh -c रैपर नहीं होता। Compose का संदर्भ (reference) इस बारे में स्पष्ट है: command फ़ील्ड इमेज में परिभाषित SHELL संदर्भ के भीतर नहीं चलती है, इसलिए यदि आपको शेल सुविधाओं की आवश्यकता है, तो आपको स्वयं एक शेल को इनवोके (invoke) करना होगा।

यही कारण है कि command: echo "hello $$HOSTNAME" शाब्दिक टेक्स्ट hello $HOSTNAME को प्रिंट करता है। किसी भी शेल ने उस स्ट्रिंग को नहीं देखा, इसलिए किसी भी चीज़ ने उसे एक्सपैंड (expand) नहीं किया। जब आपको शेल की आवश्यकता हो, तो उसे स्पष्ट रूप से मांगें:

services:
  demo:
    image: alpine:3.20
    command: /bin/sh -c 'echo "hello $$HOSTNAME"'

Signals, PID 1, और एक clean docker compose down

docker compose stop और docker compose down प्रत्येक container के अंदर PID 1 को SIGTERM भेजते हैं, stop_grace_period की प्रतीक्षा करते हैं, और फिर SIGKILL भेजते हैं। डिफ़ॉल्ट grace period 10 सेकंड का होता है।

Linux में PID 1 विशेष होता है। Kernel PID 1 पर signal की डिफ़ॉल्ट कार्रवाई लागू नहीं करता है, इसलिए जो process कोई SIGTERM handler install नहीं करती, वह PID 1 के रूप में चलने पर SIGTERM को अनदेखा कर देती है। यह पूरे grace period तक वहीं रहती है और फिर उसे जबरन kill कर दिया जाता है, जिससे कोई भी open connection या uncommitted transaction बीच में ही कट जाता है।

आपके program के सामने एक shell होने से इसकी संभावना बढ़ जाती है, क्योंकि shell ही PID 1 होता है और अधिकांश shells child process को signals forward नहीं करते हैं। कुछ shells खुद को -c string के अंतिम command से replace कर लेते हैं, इसलिए कभी-कभी आपका program PID 1 तक पहुँच जाता है। यह shell और सटीक string पर निर्भर करता है, इसलिए अनुमान न लगाएँ। इसे पढ़ें:

docker compose exec -T web cat /proc/1/cmdline | tr '\0' ' '; echo

यदि PID 1 आपके program के बजाय /bin/sh -c ... के रूप में print होता है, तो इसके दो समाधान हैं। Image में exec form का उपयोग करें, या shell को बनाए रखें और exec के साथ process को सौंप दें:

services:
  web:
    image: myapp:1.4
    command: /bin/sh -c 'exec myapp --config /etc/myapp.toml'

exec shell process को child fork करने के बजाय आपके program से replace कर देता है, इसलिए आपका program PID 1 बन जाता है और signal प्राप्त करता है।

कुछ programs children spawn करते हैं और उन्हें कभी reap नहीं करते, जिससे zombie processes रह जाती हैं, क्योंकि PID 1 ही reaper भी होता है। Compose में इसके लिए एक switch है:

services:
  web:
    image: myapp:1.4
    init: true
    stop_grace_period: 30s

init: true PID 1 के रूप में एक छोटा init process चलाता है जो आपके process को signals forward करता है और children को reap करता है। stop_grace_period एक धीमी shutdown प्रक्रिया को अधिक समय देता है। यदि आपका program किसी अलग signal की अपेक्षा करता है, तो stop_signal: SIGQUIT उसे बदल देता है जिसे Compose भेजता है। देखें कि एक image पहले से ही docker image inspect --format '{{.Config.StopSignal}}' nginx:1.27 के साथ क्या मांगती है।

एक stack जहाँ docker compose down हमेशा प्रति service दस सेकंड लेता है, वह आपको बता रहा है कि कोई भी SIGTERM को handle नहीं कर रहा है। tooling को दोष देने से पहले इसे ठीक करें, और docker compose down और stop के बीच का अंतर देखें कि प्रत्येक subcommand क्या हटाता है।

वही exec-versus-shell का अंतर एक और जगह सामने आता है। test: ["CMD", "curl", "-f", "http://localhost/"] के रूप में लिखा गया healthcheck सीधे binary को चलाता है, जबकि test: ["CMD-SHELL", "curl -f http://localhost/ || exit 1"] एक shell के माध्यम से चलता है ताकि || का कोई अर्थ निकल सके। ऐसे Compose healthchecks लिखना जो ईमानदारी से विफल हों उस क्षेत्र के बाकी हिस्सों को कवर करता है।

Official image में एक flag जोड़ना

ज्यादातर पाठक इसी के लिए आए हैं। आप postgres पर एक अतिरिक्त flag चाहते हैं, और आपको initialisation script को बिल्कुल नहीं छेड़ना है।

services:
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    volumes:
      - pgdata:/var/lib/postgresql/data
    command: postgres -c max_connections=200 -c shared_buffers=256MB

volumes:
  pgdata:

केवल command: बदला है, इसलिए docker-entrypoint.sh अभी भी चलता है और आपके द्वारा दिए गए निर्देश को ही exec करता है। परिणाम को मान लेने के बजाय उसकी जाँच करें:

docker compose up -d db
docker compose exec -T db psql -U postgres -c 'show max_connections;'

Output में 200 दिखना चाहिए। यदि यह अभी भी 100 दिखाता है, तो docker compose config चलाएँ और पुष्टि करें कि जो command आप चाहते हैं, वह merged output में मौजूद है। Compose, override files को सीधे command को replace करके merge करता है, न कि उसमें append करके, इसलिए दूसरी file जो command: को set करती है, वह चुपचाप जीत जाती है।

ऊपर दिया गया ${POSTGRES_PASSWORD}, container के अस्तित्व में आने से पहले, host पर आपकी .env file से Compose द्वारा expand किया जाता है। Compose में Env files और secrets इस बात को कवर करता है कि वह value सुरक्षित रूप से कहाँ रह सकती है।

docker compose run के साथ एक बार चलने वाला माइग्रेशन चलाना

docker compose run उसी सर्विस परिभाषा से एक नया कंटेनर बनाता है और कमांड को उस चीज़ से बदल देता है जिसे आप सर्विस नाम के बाद टाइप करते हैं। इमेज का entrypoint अभी भी चलता है, इसलिए कंटेनर बिल्कुल वैसे ही तैयार होता है जैसे लंबे समय तक चलने वाला कंटेनर होता है।

docker compose run --rm app python manage.py migrate
  • --rm कमांड के बाहर निकलने पर कंटेनर को हटा देता है। इसके बिना, हर रन एक रुका हुआ कंटेनर पीछे छोड़ देता है, जो docker compose ps -a में दिखाई देता है।
  • पोर्ट पब्लिश नहीं किए जाते हैं। एक run कंटेनर सर्विस के ports: को अनदेखा करता है जब तक कि आप --service-ports न जोड़ें, इसलिए यह पहले से चल रही सर्विस के साथ टकरा नहीं सकता है।
  • निर्भरताएँ (dependencies) पहले शुरू होती हैं। depends_on में मौजूद कोई भी चीज़ आपके कमांड से पहले शुरू हो जाती है, और --no-deps इसे छोड़ देता है।
  • कंटेनर को myproject-app-run-9f2c1a जैसा एक जनरेट किया हुआ नाम मिलता है, इसलिए यह सर्विस कंटेनर के साथ कभी नहीं टकराता है।

entrypoint को भी बदलने के लिए, इसके लिए एक फ्लैग है:

docker compose run --rm --entrypoint /bin/sh app -c 'python manage.py migrate'

परिणामी तर्क सूची (argument list) /bin/sh -c 'python manage.py migrate' है, क्योंकि सर्विस नाम के बाद के शब्द अभी भी कमांड ही हैं। docker compose exec दूसरा टूल है और यह अलग तरह से काम करता है: यह पहले से चल रहे कंटेनर के अंदर एक प्रोसेस चलाता है, और यह entrypoint: और command: दोनों को पूरी तरह से अनदेखा कर देता है। ऐसे कार्य के लिए run का उपयोग करें जिसे एक नए कंटेनर की आवश्यकता है, और लाइव कंटेनर के अंदर देखने के लिए exec का उपयोग करें। Compose कमांड चीट शीट बाकी सब-कमांड्स को एक साथ रखती है।

मेरा container तुरंत exit क्यों हो जाता है?

सबसे पहले exit code देखें, क्योंकि इससे कारण का पता जल्दी चलता है।

docker compose ps -a
docker compose logs app

Exit code 0 और कोई output नहीं। command चली और पूरी हो गई। इसका सबसे सामान्य कारण एक entrypoint: override है जो image के CMD को हटा देता है, जिससे entrypoint खाली argument list के साथ चलता है और उसे आगे प्रोसेस करने के लिए कुछ नहीं मिलता।

permission denied पर समाप्त होने वाली error। script में executable bit सेट नहीं है, आमतौर पर इसलिए क्योंकि repository में फाइल पर यह bit कभी सेट ही नहीं किया गया था। इसे build के समय COPY --chmod=0755 entrypoint.sh /entrypoint.sh का उपयोग करके सेट करें।

ऐसी फाइल के लिए no such file or directory error जिसे आप image में स्पष्ट देख सकते हैं। script में Windows line endings हैं। इसकी पहली लाइन #!/bin/sh और एक carriage return byte के रूप में पढ़ी जाती है, इसलिए kernel उस नाम के interpreter को ढूँढता है जिसमें वह byte भी शामिल हो और उसे कुछ नहीं मिलता। dos2unix entrypoint.sh चलाएँ, फिर .gitattributes में * text eol=lf जोड़ें ताकि यह समस्या दोबारा न आए।

executable file not found in $PATH command: में नामित binary image में मौजूद नहीं है, या आपने cd जैसा shell built-in लिखा है जहाँ केवल एक वास्तविक program ही हो सकता है।

ऐसे image का shell प्राप्त करना जिसका entrypoint विफल हो जाता है

जब आप कुछ भी जांच सकें, उससे पहले ही entrypoint बंद हो जाए, तो उसे बदल दें:

docker compose run --rm --entrypoint /bin/sh app

यदि यह executable file not found in $PATH लौटाता है, तो image में कोई shell नहीं है। Distroless और scratch आधारित images में अक्सर shell नहीं होता है। आप अभी भी entrypoint शुरू किए बिना बाहर से filesystem को पढ़ सकते हैं:

docker create --name probe myapp:1.4
docker export probe | tar -tv | head -40
docker rm probe

जब आपको container को चालू रखने की आवश्यकता हो ताकि आप बार-बार उससे जुड़ सकें, तो उसे एक ऐसी प्रक्रिया पर पार्क करें जो कभी समाप्त नहीं होती है। इसे एक override file में डालें जिसे आप commit नहीं करते हैं:

services:
  app:
    entrypoint: ["tail", "-f", "/dev/null"]
    command: []

command: [] की सख्ती से आवश्यकता नहीं है, क्योंकि entrypoint: सेट करने से image का CMD पहले ही साफ हो चुका है, लेकिन इसे लिखना बाद में फाइल पढ़ने वाले व्यक्ति के लिए उद्देश्य को दर्ज करता है। इसे चालू करें और अंदर जाएं:

docker compose -f compose.yaml -f compose.debug.yaml up -d app
docker compose exec app /bin/sh

अब वास्तविक entrypoint को मैन्युअल रूप से चलाएं और देखें कि यह कहां रुकता है। यह आपको आधे सेकंड पहले बंद हुए container के बजाय अपने terminal पर त्रुटि संदेश देता है। यदि आप अभी भी अपना पहला stack बना रहे हैं, तो VPS पर पहला Compose stack उस फाइल लेआउट को कवर करता है जिसे ऊपर दी गई सभी बातें मानती हैं।

FAQ

docker compose up के तुरंत बाद मेरा container exit क्यों हो जाता है?

Exit code के लिए docker compose ps -a की जाँच करें। बिना किसी output के Exit 0 का सामान्य अर्थ है कि आपने service पर entrypoint: सेट किया है, जिसने image के CMD को भी हटा दिया है। इस कारण entrypoint खाली argument list के साथ चला और समाप्त हो गया। command: के साथ arguments को वापस जोड़ें। permission denied पर समाप्त होने वाली error का अर्थ है कि entrypoint script में executable bit नहीं है। किसी मौजूद file के लिए no such file or directory पर समाप्त होने वाली error का अर्थ है कि script में Windows line endings हैं, इसलिए उसकी shebang line एक ऐसे interpreter का नाम लेती है जो वहां मौजूद नहीं है।

क्या Compose में entrypoint सेट करने से image का CMD हट जाता है?

हाँ। यदि entrypoint non-null है, तो Compose image द्वारा घोषित किसी भी default command को ignore कर देता है। यह documented व्यवहार है और यह docker run --entrypoint से मेल खाता है। इसका कारण यह है कि image का CMD उस image के ENTRYPOINT के लिए arguments के रूप में लिखा जाता है, इसलिए एक बार जब आप entrypoint बदल देते हैं, तो पुराने arguments किसी काम के नहीं रहते। यदि नए entrypoint को अभी भी arguments की आवश्यकता है, तो उसी service में command: सेट करें।

क्या Compose command में दी गई string shell के माध्यम से चलती है?

नहीं। Dockerfile CMD के विपरीत, Compose command: में दी गई string को arguments में विभाजित किया जाता है और सीधे execute किया जाता है, इसमें कोई /bin/sh -c wrapper नहीं होता। इसलिए $VARIABLE को container के अंदर किसी shell द्वारा कभी expand नहीं किया जाता है। जब आपको shell की आवश्यकता हो, तो इसे स्वयं call करें, जैसा कि command: /bin/sh -c 'echo "hello $$HOSTNAME"' में दिखाया गया है। दोहराया गया $$ डॉलर चिह्न को escape करता है, ताकि Compose इसे host पर expand करने के बजाय container तक पहुँचा दे।

docker compose down एक container के लिए दस सेकंड क्यों लेता है?

Compose PID 1 को SIGTERM भेजता है, stop_grace_period (default रूप से 10 सेकंड) की प्रतीक्षा करता है, और फिर SIGKILL भेजता है। Kernel PID 1 पर default signal actions लागू नहीं करता है, इसलिए जिस program में SIGTERM handler नहीं होता, वह signal को ignore कर देता है और हमेशा पूरी अवधि तक प्रतीक्षा करता है। docker compose exec -T app cat /proc/1/cmdline | tr '\0' ' ' के साथ पता लगाएँ कि PID 1 वास्तव में क्या है। यदि यह एक shell है, तो image को exec form में बदलें या shell string के अंदर exec लिखें। यदि process ऐसे children spawn करती है जिन्हें वह कभी reap नहीं करती, तो service पर init: true सेट करें।

#docker-compose#entrypoint#command#containers#debugging