Docker Compose को boot पर auto-start कैसे कराएँ
Reboot के बाद Docker Compose services शुरू कराने के लिए restart policies सेट करें। जानें कि on-failure एक बार क्यों नहीं चलता और systemd unit कब सही है।
संक्षिप्त उत्तर
Docker Compose सेवाएँ boot पर तब शुरू होती हैं, जब दो शर्तें एक साथ पूरी हों। Docker daemon को system service के रूप में enabled होना चाहिए, और फ़ाइल में प्रत्येक service के साथ unless-stopped या always की restart policy होनी चाहिए। प्रत्येक service में restart: unless-stopped जोड़ें, एक बार docker compose up -d चलाएँ, और reboot के बाद containers अपने-आप वापस शुरू हो जाएँगे। सामान्य स्थिति में इसके अलावा कुछ आवश्यक नहीं है।
आपको systemd unit केवल तब चाहिए, जब क्रम महत्वपूर्ण हो: जैसे कोई stack mounted disk, VPN interface या network share पर निर्भर हो, जो Docker daemon शुरू होने के समय तैयार न हो। यह स्थिति वास्तविक है, और इस guide का दूसरा भाग इसे कवर करता है। यदि आप अभी भी service definitions और volumes को समझ रहे हैं, तो VPS पर Docker Compose की मूल बातें से शुरू करें और फिर वापस आएँ।
compose.yaml में restart policy सेट करें
Policy प्रत्येक service के लिए एक पंक्ति में होती है। कोई global switch नहीं है। इसलिए जिस service को आप भूल जाते हैं, वह reboot के बाद बंद रहती है, जबकि stack की बाकी services शुरू हो जाती हैं।
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:इसे लागू करें और फिर running container से policy पढ़ें:
docker compose up -d
docker inspect -f '{{.HostConfig.RestartPolicy.Name}}' $(docker compose ps -q app)इससे unless-stopped प्रिंट होता है। यदि no प्रिंट होता है, तो file edit की गई थी, लेकिन container को फिर से बनाया नहीं गया।
यह सबसे आम failure है। restart policy container में stored होती है, YAML file में नहीं। compose.yaml को edit करने से पहले से मौजूद container पर कोई प्रभाव नहीं पड़ता। docker compose restart भी मदद नहीं करता, क्योंकि यह उसी container object को बिना configuration बदले stop और start करता है। केवल docker compose up -d file की तुलना running containers से करता है, policy में बदलाव पहचानता है और containers को फिर से बनाता है।
जिस container को आप अभी फिर से नहीं बनाना चाहते, उसकी policy को सीधे बदलें:
docker update --restart unless-stopped my-containerYAML file को भी edit करें। docker update live container को बदलता है, और अगला docker compose up -d file को पढ़कर पुरानी value वापस लागू कर देगा।
प्रत्येक restart value वास्तव में क्या करता है
Docker चार values परिभाषित करता है। इनके बीच का अंतर केवल machine के reboot होने या daemon के restart होने पर दिखाई देता है।
nodefault है। Container किसी भी परिस्थिति में अपने-आप restart नहीं होता।alwayscontainer के stop होते ही उसे restart करता है। यदि आपने इसे manually stop किया है, तो Docker daemon के अगली बार start होने पर यह फिर चलने लगता है। यह अक्सर unexpected होता है: जिस container को आपने पिछले सप्ताह जानबूझकर stop किया था, वह reboot के बाद फिर चल रहा होता है।unless-stopped,alwaysकी तरह काम करता है। अंतर यह है कि manually stop किया गया container daemon के restart के बाद भी stopped रहता है। उस service के लिए यही value चुनें जिसे आप maintenance के लिए कभी-कभी बंद करते हैं।on-failurecontainer को केवल तब restart करता है जब वह non-zero exit code के साथ exit होता है। आप attempts की अधिकतम संख्या निर्धारित कर सकते हैं, जैसा किrestart: on-failure:3में है।
ऐसे stack के लिए जो server के up होने पर हमेशा चलना चाहिए, unless-stopped सही default है। always केवल तब चुनें जब आप ऐसा container चाहते हों जिसे down छोड़े रखना कठिन हो।
पुनरारंभ क्यों: on-failure रीबूट के बाद लागू नहीं रहता
कई लोग on-failure चुनते हैं क्योंकि यह सावधानीपूर्ण विकल्प लगता है। फिर उन्हें पहली रीबूट के बाद सभी कंटेनर बंद मिलते हैं। इसका कारण इसकी परिभाषा में है। on-failure केवल एक स्थिति पर प्रतिक्रिया करता है: कंटेनर प्रक्रिया का त्रुटि कोड के साथ समाप्त होना।
रीबूट कोई त्रुटि नहीं है। जब host बंद होता है, systemd docker.service को रोकता है और daemon प्रत्येक कंटेनर को जानबूझकर रोकता है। कंटेनर विफल नहीं हुआ, इसलिए policy के पास प्रतिक्रिया करने के लिए कुछ नहीं है। host के दोबारा शुरू होने पर daemon उन कंटेनरों की स्थिति देखता है जिन्हें फिर से शुरू करना आवश्यक है। साफ़ तौर पर रोका गया on-failure कंटेनर उनमें शामिल नहीं होता। वह exited स्थिति में रहता है।
आप इसे सीधे देख सकते हैं। किसी service पर restart: on-failure सेट करें, docker compose up -d चलाएँ, रीबूट करें, फिर चलाएँ:
docker compose ps -aservice की state Exited और status Exited (0) 2 minutes ago जैसा दिखाई देगा। कुछ भी खराब नहीं है और किसी त्रुटि को log नहीं किया जाता। इसी कारण इसका निदान करना कठिन होता है। policy ने ठीक वही किया जो उसकी परिभाषा में है।
on-failure अभी भी उपयोगी है। यह ऐसे कंटेनर के लिए उपयुक्त है जो कोई job चलाता है और crash हो सकता है, जहाँ आप पुनः प्रयासों की सीमित संख्या चाहते हैं और restart loop नहीं चाहते। रीबूट के बाद लंबे समय तक चलने वाली service को सक्रिय रखने के लिए यह सही tool नहीं है।
Restart policies केवल तभी काम करती हैं जब Docker service boot पर शुरू हो
Restart policies को Docker daemon लागू करता है। यदि daemon शुरू नहीं होता, तो कुछ भी लागू नहीं होता। इसकी जाँच करें:
systemctl is-enabled docker
systemctl is-enabled containerdदोनों को enabled प्रिंट करना चाहिए। Docker के official repository के packages install के समय इन्हें enable करते हैं, इसलिए fresh server पर यह सामान्यतः सफल होता है। यदि इनमें से कोई disabled प्रिंट करे, तो इसे ठीक करें:
sudo systemctl enable --now docker containerdयहाँ एक महत्वपूर्ण बात समझना आवश्यक है। Ubuntu में docker.socket भी शामिल होता है, जो पहली बार कोई प्रक्रिया Docker API से संपर्क करने पर daemon को मांग पर शुरू करता है। लोग docker.socket को enabled देखते हैं, मान लेते हैं कि daemon भी covered है, और memory बचाने के लिए docker.service को disable कर देते हैं। Boot के समय API को call करने वाला कोई नहीं होता। इसलिए socket को access नहीं किया जाता, daemon शुरू नहीं होता, और जब तक आप अपना पहला docker command type नहीं करते, कोई container शुरू नहीं होता। Socket activation, docker.service के enabled होने का विकल्प नहीं है।
systemd unit बेहतर विकल्प कब है
Restart policies में सिस्टम के बाकी हिस्सों के साथ क्रम निर्धारित करने की सुविधा नहीं होती। Daemon शुरू होता है और जितनी जल्दी संभव हो, आपके containers शुरू कर देता है। यदि आपका stack किसी अलग volume, NFS (network file system) share या encrypted disk से किसी directory को bind-mount करता है, तो वह path उपलब्ध होने से पहले containers शुरू हो सकते हैं। Docker mount point पर खाली directory बनाकर उसके साथ container शुरू कर देता है। इसके बाद आपका database बिना data के शुरू होता है।
इनमें से कोई भी स्थिति लागू होने पर systemd unit लिखें। Stack के लिए पहले किसी mount, VPN interface या किसी अन्य unit का तैयार होना आवश्यक है। आप चाहते हैं कि systemctl stop myapp और systemctl start myapp उसी तरह काम करें, जैसे server पर हर दूसरी service के लिए करते हैं। या आप चाहते हैं कि shutdown के दौरान daemon के साथ जबरन बंद किए जाने के बजाय stack को साफ़ तरीके से बंद किया जाए। यदि systemd units आपके लिए नई हैं, तो systemd service और timer लिखना file format की अधिक विस्तृत जानकारी देता है।
systemd यूनिट लिखना
स्टैक को home directory के बाहर एक निश्चित path पर रखें। /srv/myapp अच्छा विकल्प है, क्योंकि 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स्वस्थ यूनिट Active: active (exited) दिखाती है। पहली बार देखने पर यह गलत लगता है। यह सही है: Type=oneshot के साथ RemainAfterExit=yes का अर्थ है कि यूनिट ने अपना command चलाया, command पूरा हो गया, और systemd यूनिट को active चिह्नित रखता है, ताकि shutdown के समय ExecStop चले।
हर line का एक उद्देश्य है। Requires=docker.service का अर्थ है कि यूनिट dead socket पर docker compose चलाने के बजाय तुरंत विफल हो जाती है। After= क्रम निर्धारित करता है, क्योंकि केवल Requires= ऐसा नहीं करता। RequiresMountsFor= systemd को उस path के लिए mount unit शामिल करने और उसके लिए प्रतीक्षा करने को कहता है। यही वह मुख्य कारण है जिसके लिए restart policy के बजाय यूनिट का उपयोग किया जाता है। TimeoutStartSec=0 systemd को तब तक start job समाप्त करने से रोकता है, जब तक बड़ी image pull हो रही हो।
दोनों mechanisms को साथ रखने पर एक बात ध्यान रखें। Docker का documentation restart policies को host process manager के साथ मिलाने की सलाह नहीं देता। यह चेतावनी ऐसे process manager के बारे में है, जो स्वयं container process की निगरानी करता है और daemon के साथ उसी process को restart करने का प्रयास करता है। Type=oneshot यूनिट किसी चीज़ की निगरानी नहीं करती, इसलिए इस यूनिट के साथ compose file में restart: unless-stopped रखना ठीक है और यही वांछित है। systemd boot के समय क्रम संभालता है, और daemon ऐसे container को संभालता है जो सुबह 3 बजे crash हो जाता है।
वास्तविक reboot से सत्यापित करें
वास्तविक परीक्षण का कोई विकल्प नहीं है। systemctl restart docker mount ordering की जाँच नहीं करता, और docker compose down के बाद docker compose up -d चलाने से boot से संबंधित किसी भी चीज़ की जाँच नहीं होती।
sudo rebootप्रतीक्षा करें, फिर से connect करें और इस क्रम में जाँच करें:
uptime
systemctl is-active docker
docker compose psuptime पुष्टि करता है कि आप ऐसी मशीन देख रहे हैं जो वास्तव में reboot हुई है। stack directory से चलाया गया docker compose ps, हर service को running स्थिति में और मशीन के uptime के करीब uptime के साथ सूचीबद्ध करना चाहिए। Exited दिखाने वाली service की जाँच करें।
यदि कोई चीज़ शुरू नहीं हुई, तो daemon log boot window को कवर करता है:
journalctl -u docker.service -b --no-pager | tail -50unit द्वारा managed stack के लिए, journalctl -u myapp.service -b --no-pager boot के दौरान का सटीक docker compose output दिखाता है। इसमें failed image pull या missing .env file भी शामिल होती है।
वे चीज़ें जो auto-start को चुपचाप विफल कर देती हैं
docker compose run से बनाए गए containers को file से restart policy कभी नहीं मिलती। Compose उन्हें one-off containers मानता है। यदि कोई service अपनी policy को अनदेखा करती हुई लगे, तो जाँचें कि उसे up के बजाय run से शुरू किया गया था।
किसी volume या env_file entry में relative path का समाधान compose file की directory के आधार पर होता है। यह आपके shell से काम करता है और उस unit से भी काम करता है जो WorkingDirectory सेट करती है। यह ऐसी unit से विफल होता है जिसमें यह सेट नहीं है, क्योंकि तब working directory / होती है।
Rootless Docker अलग मामला है। daemon user service के रूप में चलता है और user service तब रुक जाती है जब उस user का आखिरी session समाप्त हो जाता है। इसे user के लिए enable करें और इसे किसी user के logged in न होने पर भी चलने दें:
systemctl --user enable docker
sudo loginctl enable-linger $USERenable-linger के बिना, log out करने पर rootless daemon बंद हो जाता है और containers भी उसके साथ बंद हो जाते हैं। यह ठीक ऐसा दिखाई देता है जैसे restart policy खराब हो।
एक अंतिम बात। Automatic security updates किसी निश्चित समय पर server को reboot कर सकते हैं। यह तभी उपयोगी है जब आपका stack अपने-आप वापस शुरू हो जाए। नई machine पर इसे सेट करना पहली घंटे की बाकी कार्यवाही के साथ नए VPS पर पहले दस मिनट में करना चाहिए।
FAQ
restart: always और restart: unless-stopped में क्या अंतर है?
दोनों तब container को फिर से शुरू करते हैं, जब वह स्वयं रुकता है। अंतर तब होता है, जब आप container को स्वयं रोकते हैं। always के साथ, Docker daemon के अगली बार शुरू होने पर container फिर शुरू हो जाता है। इसलिए reboot आपके मैन्युअल stop को रद्द कर देता है। unless-stopped के साथ, daemon याद रखता है कि container को जानबूझकर रोका गया था और उसे फिर शुरू नहीं करता। unless-stopped का उपयोग करें, जब तक कि आपको विशेष रूप से ऐसा container न चाहिए जो बंद ही रहे।
मैंने restart: unless-stopped जोड़ा, लेकिन reboot के बाद container फिर भी शुरू नहीं होता। क्यों?
यह policy file में नहीं, container पर लागू होती है। पहले से मौजूद container YAML को संपादित करने पर update नहीं होता। Compose से उसे फिर बनाने के लिए docker compose up -d चलाएँ। फिर docker inspect -f '{{.HostConfig.RestartPolicy.Name}}' $(docker compose ps -q app) से पुष्टि करें। यदि उसका output no है, तो container आपके संपादन से पहले बनाया गया था। दूसरा सामान्य कारण docker.service का enabled न होना है। इसे systemctl is-enabled docker से जाँच सकते हैं।
यदि मैं पहले से restart policies का उपयोग कर रहा हूँ, तो क्या मुझे systemd unit की आवश्यकता है?
आमतौर पर नहीं। जिस stack को केवल network की आवश्यकता होती है, उसके लिए restart policy पर्याप्त है। अधिकांश stack इसी श्रेणी में आते हैं। जब container किसी ऐसी निर्भरता पर आधारित हों जो Docker daemon के शुरू होने पर तैयार न हो, तब unit जोड़ें। उदाहरण के लिए, यह external disk, encrypted volume, NFS share या VPN interface हो सकता है। Unit आपको After= और RequiresMountsFor= के माध्यम से startup 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 उसे फिर शुरू कर देगी।