Docker Compose services को auto-start कैसे करें
Docker Compose services को reboot के बाद स्वचालित रूप से शुरू करने के लिए restart: always का उपयोग करें। जानें कि on-failure क्यों विफल होता है और systemd unit कब आवश्यक है।
संक्षिप्त उत्तर
Docker Compose services दो शर्तों के एक साथ पूरा होने पर boot के समय start होती हैं। Docker daemon को system service के रूप में enabled होना चाहिए, और file में प्रत्येक service के पास unless-stopped या always की restart policy होनी चाहिए। प्रत्येक service में restart: unless-stopped जोड़ें, एक बार docker compose up -d चलाएं, और reboot के बाद containers अपने आप वापस आ जाएंगे। सामान्य स्थिति के लिए इसके अलावा कुछ और आवश्यक नहीं है।
आपको systemd unit की आवश्यकता केवल तब होती है जब क्रम (order) मायने रखता है: जैसे कि कोई stack जो किसी mounted disk, VPN interface, या network share पर निर्भर हो, जो Docker daemon के start होने के समय तैयार न हो। यह स्थिति वास्तविक है, और इस guide का दूसरा भाग इसे कवर करता है। यदि आप अभी भी service definitions और volumes के बारे में सीख रहे हैं, तो VPS पर Docker Compose की बुनियादी बातें से शुरुआत करें और फिर वापस आएं।
Set the restart policy in compose.yaml
The policy is one line per service. There is no global switch, so a service you forget stays down after the reboot while the rest of the stack comes up.
services:
app:
image: nginx:1.27
restart: unless-stopped
ports:
- "8080:80"
db:
image: postgres:16
restart: unless-stopped
environment:
POSTGRES_PASSWORD: change-me
volumes:
- dbdata:/var/lib/postgresql/data
volumes:
dbdata:Apply it and then read the policy back off the running container:
docker compose up -d
docker inspect -f '{{.HostConfig.RestartPolicy.Name}}' $(docker compose ps -q app)That prints unless-stopped. If it prints no, the file was edited but the container was never recreated.
This is the single most common failure. The restart policy is stored on the container, not in the YAML file. Editing compose.yaml changes nothing about a container that already exists. docker compose restart does not help either, because it stops and starts the same container object without touching its configuration. Only docker compose up -d compares the file to the running containers, notices the policy changed, and recreates them.
For a container you do not want to recreate right now, change the policy in place:
docker update --restart unless-stopped my-containerStill edit the YAML file as well. docker update changes the live container, and the next docker compose up -d will read the file and put the old value back.
प्रत्येक restart मान वास्तव में क्या करता है
Docker चार मान परिभाषित करता है, और उनके बीच का अंतर केवल तब दिखाई देता है जब मशीन रीबूट होती है या daemon को रीस्टार्ट किया जाता है।
noडिफ़ॉल्ट मान है। कंटेनर किसी भी परिस्थिति में स्वचालित रूप से रीस्टार्ट नहीं होता है।alwaysकंटेनर के रुकने पर उसे हर बार रीस्टार्ट करता है। यदि आपने इसे मैन्युअल रूप से रोका है, तो भी अगली बार Docker daemon शुरू होने पर यह वापस आ जाएगा। यह अक्सर आश्चर्यजनक होता है: पिछले सप्ताह आपके द्वारा जानबूझकर रोका गया कंटेनर रीबूट के बाद फिर से चल रहा होता है।unless-stoppedका व्यवहारalwaysजैसा ही है, सिवाय इसके कि मैन्युअल रूप से रोका गया कंटेनर daemon रीस्टार्ट होने पर भी रुका रहता है। यदि आप किसी सर्विस को रखरखाव के लिए कभी-कभी बंद करते हैं, तो यह मान आपके लिए उपयुक्त है।on-failureकंटेनर को केवल तभी रीस्टार्ट करता है जब वह non-zero exit code के साथ बंद होता है। आपrestart: on-failure:3की तरह प्रयासों की संख्या सीमित कर सकते हैं।
ऐसी stack के लिए जिसे सर्वर चालू होने पर हमेशा चलते रहना चाहिए, unless-stopped सही डिफ़ॉल्ट है। always को केवल तभी चुनें जब आप चाहते हैं कि कंटेनर बंद न रहे।
reboot के बाद on-failure काम क्यों नहीं करता है
बहुत से लोग on-failure चुनते हैं क्योंकि यह सुरक्षित लगता है, लेकिन फिर वे पाते हैं कि पहला reboot होते ही हर container रुक गया है। इसका कारण इसकी परिभाषा में है। on-failure केवल एक ही स्थिति पर प्रतिक्रिया करता है: जब container process किसी error code के साथ बंद होती है।
Reboot कोई error नहीं है। जब host बंद होता है, तो systemd docker.service को stop कर देता है, और daemon जानबूझकर प्रत्येक container को बंद कर देता है। Container विफल नहीं हुआ है, इसलिए policy के पास प्रतिक्रिया करने के लिए कुछ नहीं है। वापस चालू होने पर, daemon उन containers को देखता है जिन्हें उसे फिर से शुरू करना है, और एक on-failure container जिसे व्यवस्थित तरीके से बंद किया गया था, वह उनमें शामिल नहीं होता है। यह exited स्थिति में ही रहता है।
आप इसे सीधे देख सकते हैं। किसी service पर restart: on-failure सेट करें, docker compose up -d चलाएं, reboot करें, और फिर यह चलाएं:
docker compose ps -aService Exited स्थिति के साथ सूचीबद्ध होती है और इसका status Exited (0) 2 minutes ago जैसा दिखता है। कुछ भी टूटा नहीं है और कुछ भी error के रूप में log नहीं हुआ है, यही कारण है कि इसका निदान करना कठिन होता है। Policy ने बिल्कुल वैसा ही किया जैसा वह कहती है।
on-failure अभी भी उपयोगी है। यह उस container के लिए उपयुक्त है जो कोई job चलाता है और crash हो सकता है, जहाँ आप सीमित संख्या में retries चाहते हैं और कोई restart loop नहीं चाहते हैं। यह reboots के दौरान long-running service को जीवित रखने के लिए गलत tool है।
Restart policies केवल तभी काम करती हैं जब Docker service boot के समय start हो
Restart policies को Docker daemon द्वारा लागू किया जाता है। यदि daemon start नहीं होता है, तो कोई भी policy लागू नहीं होगी। इसकी जाँच करें:
systemctl is-enabled docker
systemctl is-enabled containerdदोनों को enabled प्रिंट करना चाहिए। Docker के आधिकारिक repository से प्राप्त packages install होते समय इन्हें enable कर देते हैं, इसलिए एक नए server पर यह आमतौर पर सही रहता है। यदि कोई भी disabled प्रिंट करता है, तो इसे ठीक करें:
sudo systemctl enable --now docker containerdयहाँ एक ऐसी समस्या है जिसे समझना आवश्यक है। Ubuntu में docker.socket भी होता है, जो Docker API से पहली बार संपर्क होने पर daemon को start कर देता है। लोग docker.socket को enabled देखते हैं, मान लेते हैं कि daemon सुरक्षित है, और memory बचाने के लिए docker.service को disable कर देते हैं। Boot के समय, कोई भी API को call नहीं करता है, इसलिए socket को कभी touch नहीं किया जाता, daemon कभी start नहीं होता, और जब तक आप अपना पहला docker command टाइप नहीं करते, तब तक कोई भी container start नहीं होता है। Socket activation, docker.service के enabled होने का विकल्प नहीं है।
जब systemd unit एक बेहतर विकल्प हो
Restart policies में सिस्टम के अन्य हिस्सों के साथ ordering का कोई प्रावधान नहीं होता है। Daemon start होते ही आपके containers को जितनी जल्दी हो सके, start कर देता है। यदि आपका stack किसी अलग volume, NFS (network file system) share, या encrypted disk से directory को bind-mount करता है, तो हो सकता है कि path के मौजूद होने से पहले ही containers start हो जाएं। Docker mount point पर खुशी-खुशी एक खाली directory बना देगा और container को उसके साथ start कर देगा, जिससे आपका database बिना data के ही start हो जाएगा।
जब इनमें से कोई भी स्थिति लागू हो, तो एक systemd unit लिखें। Stack को किसी mount, VPN interface, या किसी अन्य unit के पहले तैयार होने की आवश्यकता हो। आप चाहते हैं कि systemctl stop myapp और systemctl start myapp वैसे ही काम करें जैसे वे बॉक्स पर मौजूद अन्य सभी services के लिए करते हैं। या आप चाहते हैं कि shutdown के दौरान stack को daemon के साथ kill करने के बजाय व्यवस्थित तरीके से बंद किया जाए। यदि आप systemd units के लिए नए हैं, तो systemd service और timer लिखना file format के बारे में अधिक विस्तार से जानकारी देता है।
systemd unit लिखना
Stack को home directory के बाहर एक निश्चित path पर रखें। /srv/myapp एक अच्छा विकल्प है, क्योंकि जो unit किसी के login करने से पहले चलती है, उसे /home पढ़ने की आवश्यकता नहीं होती।
/etc/systemd/system/myapp.service बनाएँ:
[Unit]
Description=myapp docker compose stack
Requires=docker.service
After=docker.service network-online.target
Wants=network-online.target
RequiresMountsFor=/srv/myapp/data
[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/srv/myapp
ExecStart=/usr/bin/docker compose up -d --remove-orphans
ExecStop=/usr/bin/docker compose down
TimeoutStartSec=0
[Install]
WantedBy=multi-user.targetइसे enable और start करें:
sudo systemctl daemon-reload
sudo systemctl enable --now myapp.service
systemctl status myapp.serviceएक healthy unit Active: active (exited) दिखाती है। पहली बार देखने पर यह गलत लग सकता है। यह सही है: Type=oneshot के साथ RemainAfterExit=yes का मतलब है कि unit ने अपना command चला लिया है, command पूरा हो गया है, और systemd unit को active चिह्नित रखता है ताकि shutdown के समय ExecStop चल सके।
प्रत्येक line का अपना महत्व है। Requires=docker.service का मतलब है कि unit dead socket के विरुद्ध docker compose चलाने के बजाय तुरंत fail हो जाती है। After= क्रम निर्धारित करता है, क्योंकि Requires= अकेले ऐसा नहीं करता। RequiresMountsFor= systemd को उस path के लिए mount unit को pull करने और उसकी प्रतीक्षा करने के लिए मजबूर करता है, जो restart policy के बजाय unit का उपयोग करने का मुख्य कारण है। TimeoutStartSec=0 systemd को start job को तब kill करने से रोकता है जब कोई बड़ी image अभी भी pull हो रही हो।
दोनों तंत्रों को संयोजित करने पर एक टिप्पणी। Docker का documentation host process manager के साथ restart policies को मिलाने के विरुद्ध सलाह देता है। वह चेतावनी उस process manager के बारे में है जो container process की निगरानी करता है और उसे restart करता है जबकि daemon भी वही करने की कोशिश कर रहा होता है। एक Type=oneshot unit किसी चीज की निगरानी नहीं करती है, इसलिए इस unit के साथ compose file में restart: unless-stopped रखना ठीक है, और यही आप चाहते हैं। systemd boot के समय ordering को संभालता है, और daemon उस container को संभालता है जो रात के तीन बजे crash हो जाता है।
जब आप जिस चीज को जीवित रख रहे हैं वह stack के बजाय एक साधारण long-running process हो, तो unit अलग दिखती है, क्योंकि तब उसके नीचे कोई daemon नहीं होता और systemd के स्वयं के Restart= को निगरानी करनी पड़ती है; systemd के पीछे dsh headless चलाना उस स्वरूप का एक उदाहरण है, जिसमें dedicated user और journal का उपयोग शामिल है।
वास्तविक रीबूट के साथ सत्यापन करें
वास्तविक परीक्षण का कोई विकल्प नहीं है। systemctl restart docker माउंट ऑर्डरिंग का परीक्षण नहीं करता है, और docker compose down के बाद docker compose up -d चलाने से बूट प्रक्रिया के बारे में कुछ भी पता नहीं चलता है।
sudo rebootप्रतीक्षा करें, पुनः कनेक्ट करें, और इस क्रम में जाँच करें:
uptime
systemctl is-active docker
docker compose psuptime यह पुष्टि करता है कि आप उस मशीन को देख रहे हैं जो वास्तव में रीबूट हुई है। स्टैक डायरेक्टरी से चलाया गया docker compose ps, प्रत्येक सर्विस को running के रूप में सूचीबद्ध करना चाहिए, जिसका अपटाइम मशीन के अपटाइम के करीब हो। जो सर्विस Exited दिखा रही है, वही जाँच का विषय है।
यदि कोई सर्विस स्टार्ट नहीं हुई है, तो डेमन लॉग बूट विंडो को कवर करता है:
journalctl -u docker.service -b --no-pager | tail -50यूनिट द्वारा प्रबंधित स्टैक के लिए, journalctl -u myapp.service -b --no-pager बूट से सटीक docker compose आउटपुट दिखाता है, जिसमें विफल इमेज पुल या गायब .env फाइल शामिल हो सकती है। आप जिस रीबूट को शेड्यूल करते हैं, उसी पर नजर रखें, इसलिए यूनिट को उन रीबूट्स के बारे में बताने दें जिन्हें आप नहीं देख रहे हैं: एक self-hosted ntfy server पर पॉइंट की गई OnFailure= लाइन, वापस न आने वाले स्टैक को एक पुश नोटिफिकेशन में बदल देती है, बजाय इसके कि आप इसे दिनों बाद खोजें।
वे चीजें जो चुपचाप ऑटो-स्टार्ट को बाधित करती हैं
docker compose run के साथ बनाए गए कंटेनर कभी भी फाइल से restart policy नहीं लेते हैं। Compose उन्हें एक बार चलने वाले कंटेनर के रूप में देखता है। यदि कोई सर्विस अपनी पॉलिसी को अनदेखा करती हुई प्रतीत हो, तो जांचें कि क्या उसे run के बजाय up से शुरू किया गया था।
वॉल्यूम या env_file प्रविष्टि में एक relative path को compose फाइल की डायरेक्टरी के आधार पर resolve किया जाता है। यह आपके शेल से काम करता है, और यह उस यूनिट से भी काम करता है जो WorkingDirectory सेट करती है। यह बिना उस यूनिट के विफल हो जाता है, क्योंकि तब वर्किंग डायरेक्टरी / होती है।
Rootless Docker एक अलग मामला है। डेमन एक यूजर सर्विस के रूप में चलता है, और जब उस यूजर का अंतिम सेशन समाप्त होता है तो यूजर सर्विस रुक जाती है। इसे यूजर के लिए इनेबल करें और इसे बिना किसी के लॉग इन किए चलते रहने दें:
systemctl --user enable docker
sudo loginctl enable-linger $USERenable-linger के बिना, rootless डेमन आपके लॉग आउट होते ही बंद हो जाता है और कंटेनर भी उसके साथ बंद हो जाते हैं, जो बिल्कुल एक टूटी हुई restart policy जैसा दिखता है।
एक आखिरी बात। स्वचालित सुरक्षा अपडेट एक निश्चित समय पर सर्वर को रीबूट कर सकते हैं, जो केवल तभी अच्छा है यदि आपका स्टैक अपने आप वापस आ जाए। इसे एक नई मशीन पर सेट करना एक नए VPS पर पहले दस मिनट के दौरान किए जाने वाले शुरुआती कार्यों का हिस्सा होना चाहिए।
FAQ
restart: always और restart: unless-stopped में क्या अंतर है?
दोनों ही स्थिति में, जब container अपने आप बंद होता है, तो वे उसे restart कर देते हैं। इनका अंतर तब पता चलता है जब आप container को मैन्युअल रूप से बंद करते हैं। always के साथ, अगली बार जब Docker daemon start होता है, तो container फिर से शुरू हो जाता है, यानी reboot करने पर आपका मैन्युअल stop निष्प्रभावी हो जाता है। unless-stopped के साथ, daemon को याद रहता है कि container को जानबूझकर रोका गया था और वह उसे बंद ही रहने देता है। unless-stopped का उपयोग करें, जब तक कि आपको विशेष रूप से ऐसा container न चाहिए हो जो बंद न रहे।
मैंने restart: unless-stopped जोड़ा है, लेकिन reboot के बाद भी container start नहीं होता। क्यों?
यह policy container पर लागू होती है, न कि file पर। YAML file में बदलाव करने से पहले से मौजूद container update नहीं होते। docker compose up -d चलाएं ताकि Compose उसे फिर से बना सके, फिर docker inspect -f '{{.HostConfig.RestartPolicy.Name}}' $(docker compose ps -q app) के साथ पुष्टि करें। यदि वह no प्रिंट करता है, तो इसका मतलब है कि container आपके बदलाव से पहले का बना हुआ है। दूसरा सामान्य कारण docker.service का enabled न होना है, जिसे आप systemctl is-enabled docker के साथ जाँच सकते हैं।
यदि मैं पहले से ही restart policies का उपयोग कर रहा हूँ, तो क्या मुझे systemd unit की आवश्यकता है?
आमतौर पर नहीं। ऐसी stack के लिए restart policy पर्याप्त है जिसे केवल network की आवश्यकता होती है, जो कि अधिकांश stacks के लिए सही है। unit तब जोड़ें जब containers किसी ऐसी चीज़ पर निर्भर हों जो Docker daemon के start होने पर तैयार नहीं होती, जैसे कि external disk, encrypted volume, NFS share, या VPN interface। unit आपको After= और RequiresMountsFor= के माध्यम से क्रम (ordering) निर्धारित करने की सुविधा देती है, जिसे restart policy के जरिए नहीं किया जा सकता।
मैं stack को स्थायी रूप से कैसे रोकूँ ताकि वह अगले reboot पर वापस न आए?
unless-stopped के साथ, docker compose stop पर्याप्त है, क्योंकि मैन्युअल रूप से रोके गए container को daemon restart होने पर फिर से शुरू नहीं किया जाता है। always के साथ, केवल stop करना पर्याप्त नहीं है और reboot के बाद container वापस आ जाता है। या तो docker compose down चलाएं, जो containers को हटा देता है, या पहले docker update --restart no my-container के साथ policy बदलें। यदि कोई systemd unit stack को manage कर रही है, तो sudo systemctl disable myapp.service भी चलाएं, अन्यथा unit उसे फिर से start कर देगी।