Docker Compose reboot-এর পর auto-start চালু করুন
reboot-এর পর Docker Compose service চালু রাখতে restart: unless-stopped ব্যবহার করুন। on-failure একবারের reboot সামলায় না; disk বা VPN নির্ভর হলে systemd unit নিন।
সংক্ষিপ্ত উত্তর
Docker Compose service boot-এর সময় start হয়, যখন দুটি শর্ত একই সঙ্গে পূরণ হয়। Docker daemon-কে system service হিসেবে enabled থাকতে হবে, এবং file-এর প্রতিটি service-এ unless-stopped বা always restart policy থাকতে হবে। প্রতিটি service-এ restart: unless-stopped যোগ করুন, একবার docker compose up -d চালান, এবং reboot-এর পরে container-গুলো নিজে থেকেই আবার চালু হবে। সাধারণ ক্ষেত্রে এর বেশি কিছু প্রয়োজন নেই।
শুধু তখনই systemd unit প্রয়োজন, যখন ক্রম গুরুত্বপূর্ণ: যেমন এমন একটি stack, যা mounted disk, VPN interface বা network share-এর ওপর নির্ভর করে এবং Docker daemon start হওয়ার সময় সেটি প্রস্তুত থাকে না। এই পরিস্থিতি বাস্তব, এবং এই guide-এর দ্বিতীয় অংশে তা আলোচনা করা হয়েছে। আপনি যদি এখনও service definition ও volume সম্পর্কে ধারণা তৈরি করছেন, VPS-এ Docker Compose-এর প্রাথমিক বিষয়গুলো দিয়ে শুরু করে পরে ফিরে আসুন।
compose.yaml-এ restart policy সেট করুন
Policy প্রতি service-এর জন্য এক লাইনে নির্ধারণ করতে হয়। কোনো global switch নেই। তাই কোনো service যোগ করতে ভুললে reboot-এর পরে সেটি বন্ধই থাকবে, আর stack-এর বাকি অংশ চালু হয়ে যাবে।
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:এটি প্রয়োগ করুন। তারপর চলমান container থেকে policy-এর বর্তমান মান পড়ুন:
docker compose up -d
docker inspect -f '{{.HostConfig.RestartPolicy.Name}}' $(docker compose ps -q app)এতে unless-stopped দেখা যাবে। যদি no দেখা যায়, তাহলে file সম্পাদনা করা হয়েছে, কিন্তু container পুনরায় তৈরি করা হয়নি।
এটিই সবচেয়ে সাধারণ ব্যর্থতার কারণ। Restart policy YAML file-এ নয়, container-এ সংরক্ষিত থাকে। ইতিমধ্যে থাকা container-এর ক্ষেত্রে compose.yaml সম্পাদনা করলে কোনো পরিবর্তন হয় না। docker compose restart-ও সাহায্য করে না, কারণ এটি একই container object বন্ধ করে আবার চালু করে, কিন্তু তার configuration পরিবর্তন করে না। শুধু docker compose up -d file-টির সঙ্গে চলমান container-গুলোর তুলনা করে, policy পরিবর্তিত হয়েছে কি না শনাক্ত করে এবং container-গুলো পুনরায় তৈরি করে।
আপনি এখন কোনো container পুনরায় তৈরি করতে না চাইলে policy সরাসরি সেট করুন:
docker update --restart unless-stopped my-containerতবুও YAML file-টিও সম্পাদনা করুন। docker update চলমান container পরিবর্তন করে, আর পরের docker compose up -d file পড়ে আগের মানটি আবার সেট করবে।
প্রতিটি restart value আসলে কী করে
Docker চারটি value নির্ধারণ করে। এগুলোর পার্থক্য শুধু machine reboot হলে বা daemon restart হলে বোঝা যায়।
noহলো default। কোনো পরিস্থিতিতেই container স্বয়ংক্রিয়ভাবে restart হয় না।alwayscontainer বন্ধ হলেই সেটি restart করে। আপনি হাতে বন্ধ করলেও পরেরবার Docker daemon চালু হলে এটি আবার চালু হয়। এটি প্রায়ই অপ্রত্যাশিত হয়: গত সপ্তাহে ইচ্ছাকৃতভাবে বন্ধ করা container reboot-এর পরে আবার চালু হয়ে যায়।unless-stopped,always-এর মতোই কাজ করে। তবে হাতে বন্ধ করা container daemon restart-এর পরেও বন্ধ থাকে। যে service-টি maintenance-এর জন্য মাঝে মাঝে বন্ধ করেন, তার জন্য এই value ব্যবহার করুন।on-failurecontainer কেবল তখনই restart করে, যখন সেটি non-zero exit code নিয়ে বন্ধ হয়।restart: on-failure:3-এর মতো করে প্রচেষ্টার সংখ্যা সীমিত করতে পারেন।
server চালু থাকলেই যে stack চালু থাকা উচিত, তার জন্য unless-stopped সঠিক default। container যেন বন্ধ অবস্থায় পড়ে না থাকে, তা নিশ্চিত করতে চাইলে কেবল always বেছে নিন।
কেন restart: on-failure reboot-এর পরে কার্যকর থাকে না
অনেকে on-failure বেছে নেন, কারণ এটি সতর্ক পছন্দ বলে মনে হয়। পরে প্রথম reboot-এর পর সব container বন্ধ দেখতে পান। কারণটি এর সংজ্ঞাতেই আছে। on-failure শুধু একটি বিষয়ের প্রতিক্রিয়া জানায়: container process error code সহ বন্ধ হওয়া।
Reboot কোনো error নয়। host shutdown হওয়ার সময় systemd docker.service বন্ধ করে, এবং daemon প্রতিটি container ইচ্ছাকৃতভাবে বন্ধ করে। container ব্যর্থ হয়নি, তাই policy-টির প্রতিক্রিয়া জানানোর মতো কিছু নেই। host আবার চালু হওয়ার সময় daemon যে container-গুলো resume করার প্রয়োজন আছে, সেগুলো পরীক্ষা করে। পরিষ্কারভাবে বন্ধ হওয়া on-failure container সেই তালিকায় থাকে না। এটি exited state-এই থাকে।
আপনি সরাসরি বিষয়টি পরীক্ষা করতে পারেন। কোনো service-এ restart: on-failure সেট করুন, docker compose up -d চালান, reboot করুন, তারপর চালান:
docker compose ps -aservice-টি Exited state-এ এবং Exited (0) 2 minutes ago-এর মতো status-সহ তালিকাভুক্ত হবে। কিছু নষ্ট হয়নি এবং কোনো error log-ও তৈরি হয়নি। এ কারণেই বিষয়টি নির্ণয় করা কঠিন। policy-টি তার সংজ্ঞা অনুযায়ীই কাজ করেছে।
on-failure তবুও কার্যকর। এটি এমন container-এর জন্য উপযুক্ত, যা একটি job চালায় এবং crash করতে পারে। নিজের অবস্থায় restart: on-failure কোনো সীমা নির্ধারণ করে না। তাই বারবার ব্যর্থ হওয়া job-কে এটি পুনরায় চালু করতেই থাকে। সীমিত সংখ্যক retry এবং restart loop এড়াতে restart: on-failure:3-এর মতো একটি সীমা যোগ করুন। reboot-এর পর দীর্ঘ সময় চলমান service সচল রাখার জন্য এটি সঠিক পদ্ধতি নয়।
Restart policy কেবল তখনই কাজ করে, যখন boot-এর সময় Docker service চালু হয়
Restart policy Docker daemon প্রয়োগ করে। daemon চালু না হলে কোনো কিছুই policy প্রয়োগ করে না। এটি পরীক্ষা করুন:
systemctl is-enabled docker
systemctl is-enabled containerdউভয় কমান্ডের আউটপুটে enabled দেখানো উচিত। Docker-এর official repository-এর package ইনস্টলের সময় এগুলো enable করে, তাই নতুন 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-তে কোনো request না গেলে socket-এ কোনো connection হয় না, daemon চালু হয় না, এবং আপনি প্রথম docker command না দেওয়া পর্যন্ত কোনো container চালু হয় না। Socket activation, docker.service enabled থাকার বিকল্প নয়।
যখন systemd unit ব্যবহার করাই ভালো
Restart policy-তে সিস্টেমের অন্যান্য অংশের সঙ্গে startup order নির্ধারণের কোনো ব্যবস্থা নেই। daemon চালু হয় এবং যত দ্রুত সম্ভব আপনার container-গুলো চালু করে। আপনার stack যদি আলাদা volume, NFS (network file system) share বা encrypted disk-এর কোনো directory bind-mount করে, তাহলে সেই path তৈরি হওয়ার আগেই container-গুলো চালু হতে পারে। Docker mount point-এ একটি খালি directory তৈরি করে সেখানে container চালু করবে। ফলে আপনার database কোনো data ছাড়াই চালু হবে।
নিচের যেকোনো পরিস্থিতি প্রযোজ্য হলে systemd unit লিখুন। stack চালুর আগে কোনো mount, VPN interface বা অন্য unit ready থাকা দরকার। আপনি চান systemctl stop myapp এবং systemctl start myapp সার্ভারের অন্যান্য service-এর মতোই কাজ করুক। অথবা চান shutdown-এর সময় daemon-এর সঙ্গে জোর করে বন্ধ না হয়ে stack পরিষ্কারভাবে বন্ধ হোক। systemd unit আপনার জন্য নতুন হলে, systemd service এবং timer লেখা ফাইলের format সম্পর্কে আরও বিস্তারিত ব্যাখ্যা করে।
systemd unit লেখা
Stack-টিকে home directory-এর বাইরে একটি নির্দিষ্ট path-এ রাখুন। /srv/myapp ভালো পছন্দ, কারণ কেউ login করার আগেই চলা unit-এর /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সুস্থ unit Active: active (exited) দেখায়। প্রথমবার দেখলে এটি ভুল মনে হতে পারে। এটিই সঠিক: Type=oneshot-এর সঙ্গে RemainAfterExit=yes থাকার অর্থ হলো unit তার command চালিয়েছে, command শেষ হয়েছে, এবং systemd unit-টিকে active হিসেবে চিহ্নিত করে রেখেছে, যাতে shutdown-এর সময় ExecStop চলে।
প্রতিটি line-এর নির্দিষ্ট কারণ আছে। Requires=docker.service unit-টিকে দ্রুত fail করায়, যাতে মৃত socket-এর বিরুদ্ধে docker compose চালানোর চেষ্টা না করে। After= execution order নির্ধারণ করে, কারণ Requires= একা তা করে না। RequiresMountsFor= systemd-কে ওই path-এর mount unit যুক্ত করতে এবং সেটির জন্য অপেক্ষা করতে বলে। restart policy-এর বদলে unit ব্যবহারের মূল কারণ এটিই। বড় image এখনও pull হওয়ার সময় TimeoutStartSec=0 systemd-কে start job বন্ধ করা থেকে বিরত রাখে।
দুটি পদ্ধতি একসঙ্গে ব্যবহারের বিষয়ে একটি কথা। Docker-এর documentation host-level process manager-এর সঙ্গে restart policy একত্রে ব্যবহার না করার পরামর্শ দেয় এবং বলে যে এতে conflict তৈরি হয়। যে process manager নিজেই container process-কে supervise করে এবং daemon-এর একই কাজ করার সময় process-টি restart করে, conflict সেখানেই তৈরি হয়। একটি Type=oneshot unit কোনো কিছুকে supervise করে না। তাই এই unit-এর পাশাপাশি compose file-এ restart: unless-stopped রাখা নিরাপদ এবং এটিই আপনার প্রয়োজন। boot-এর সময় systemd ordering পরিচালনা করবে, আর ভোর তিনটায় কোনো container crash করলে daemon সেটি পরিচালনা করবে।
যে process-টিকে সচল রাখছেন সেটি stack-এর বদলে সরাসরি চলমান কোনো দীর্ঘস্থায়ী process হলে unit-টি ভিন্ন দেখাবে। সে ক্ষেত্রে এর নিচে কোনো daemon থাকে না, তাই systemd-এর নিজস্ব Restart=-কেই supervision করতে হয়; systemd-এর মাধ্যমে headless dsh চালানো সেই ধরনের একটি সম্পূর্ণ উদাহরণ। এতে dedicated user এবং journal-ও দেখানো হয়েছে।
বাস্তব reboot দিয়ে যাচাই করুন
বাস্তব পরীক্ষার বিকল্প নেই। systemctl restart docker mount ordering পরীক্ষা করে না, আর docker compose down-এর পরে docker compose up -d চালালেও boot-সংক্রান্ত কোনো বিষয় পরীক্ষা হয় না।
sudo rebootঅপেক্ষা করুন, পুনরায় সংযোগ করুন এবং এই ক্রমে পরীক্ষা করুন:
uptime
systemctl is-active docker
docker compose psuptime নিশ্চিত করে যে আপনি সত্যিই reboot হওয়া একটি machine দেখছেন। stack directory থেকে চালানো docker compose ps-এ প্রতিটি service-এর অবস্থা running হিসেবে দেখানো উচিত এবং uptime machine-এর uptime-এর কাছাকাছি হওয়া উচিত। কোনো service-এ Exited দেখা গেলে সেটিই আগে পরীক্ষা করুন।
কোনো service start না হলে daemon log-এ boot window-এর তথ্য থাকবে:
journalctl -u docker.service -b --no-pager | tail -50unit দিয়ে পরিচালিত stack-এর ক্ষেত্রে journalctl -u myapp.service -b --no-pager boot-এর সময়কার সঠিক docker compose output দেখায়। এর মধ্যে ব্যর্থ image pull বা অনুপস্থিত .env file-ও থাকতে পারে। আপনি যে reboot schedule করেন, সেটিই monitor করবেন। তাই যেগুলো সম্পর্কে আপনি নিজে জানতে চান না, সেগুলোর তথ্য unit-কে জানাতে দিন: একটি self-hosted ntfy server-এ নির্দেশ করা OnFailure= line stack ফিরে আসতে ব্যর্থ হলে সেটিকে এমন একটি push notification-এ পরিণত করে, যা আপনি কয়েক দিন পরে নিজে আবিষ্কার করবেন না।
স্বয়ংক্রিয়ভাবে চালু হওয়ার প্রক্রিয়া নীরবে যে কারণে ব্যর্থ হয়
docker compose run দিয়ে তৈরি container কখনও file থেকে restart policy পায় না। Compose এগুলোকে one-off container হিসেবে বিবেচনা করে। কোনো service তার policy উপেক্ষা করছে মনে হলে, সেটি up-এর পরিবর্তে run দিয়ে চালু করা হয়েছিল কি না পরীক্ষা করুন।
কোনো volume বা env_file entry-তে relative path থাকলে সেটি compose file-এর directory অনুযায়ী নির্ধারিত হয়। আপনার shell থেকে এটি কাজ করে। WorkingDirectory নির্ধারণ করা unit থেকেও এটি কাজ করে। কিন্তু WorkingDirectory ছাড়া unit থেকে এটি ব্যর্থ হয়, কারণ তখন working directory হলো /।
Rootless Docker একটি আলাদা বিষয়। daemon একটি user service হিসেবে চলে। সেই user-এর শেষ session শেষ হলে user service বন্ধ হয়ে যায়। user-এর জন্য এটি enable করুন এবং কোনো user logged in না থাকলেও service-টিকে চালু রাখার অনুমতি দিন:
systemctl --user enable docker
sudo loginctl enable-linger $USERenable-linger ছাড়া আপনি logout করলে rootless daemon বন্ধ হয়ে যায় এবং container-গুলোও বন্ধ হয়ে যায়। এতে restart policy নষ্ট হয়েছে বলে মনে হয়।
আরেকটি বিষয় আছে। Automatic security update নির্দিষ্ট সময়ে server reboot করতে পারে। আপনার stack নিজে থেকে আবার চালু হলে তবেই এটি উপকারী। নতুন machine-এ এটি configure করা প্রথম ঘণ্টার কাজের অংশ হিসেবে নতুন VPS-এ প্রথম দশ মিনিট-এর সঙ্গে করা উচিত।
FAQ
restart: always এবং restart: unless-stopped-এর মধ্যে পার্থক্য কী?
দুটিই container নিজে বন্ধ হয়ে গেলে সেটিকে আবার চালু করে। পার্থক্য দেখা যায় আপনি হাতে কোনো container বন্ধ করার পরে। always ব্যবহার করলে Docker daemon পরেরবার চালু হওয়ার সময় container আবার শুরু হয়। তাই reboot করলে আপনার হাতে করা stop কার্যকর থাকে না। unless-stopped ব্যবহার করলে daemon মনে রাখে যে container-টি ইচ্ছাকৃতভাবে বন্ধ করা হয়েছে এবং সেটিকে আর চালু করে না। কোনো container বন্ধ অবস্থায় রাখতে না চাইলে unless-stopped ব্যবহার করুন।
restart: unless-stopped যোগ করার পরও reboot-এর পরে container শুরু হয় না কেন?
এই policy file-এ নয়, container-এ সংরক্ষিত থাকে। বিদ্যমান container YAML সম্পাদনা করলে আপডেট হয় না। Compose যেন container-টি নতুন করে তৈরি করে, সে জন্য docker compose up -d চালান। তারপর docker inspect -f '{{.HostConfig.RestartPolicy.Name}}' $(docker compose ps -q app) দিয়ে নিশ্চিত করুন। সেটি যদি no দেখায়, তাহলে container-টি আপনার সম্পাদনার আগেই তৈরি হয়েছিল। আরেকটি সাধারণ কারণ হলো docker.service enabled নয়। systemctl is-enabled docker দিয়ে এটি পরীক্ষা করতে পারেন।
restart policy ব্যবহার করলে কি systemd unit প্রয়োজন?
সাধারণত নয়। যে stack-এর শুধু network প্রয়োজন, তার জন্য restart policy যথেষ্ট। অধিকাংশ stack এই শ্রেণির। Docker daemon চালু হওয়ার সময় প্রস্তুত থাকে না এমন কোনো কিছুর ওপর container নির্ভর করলে unit যোগ করুন। উদাহরণ হিসেবে external disk, encrypted volume, NFS share বা VPN interface-এর কথা বলা যায়। unit After= এবং RequiresMountsFor=-এর মাধ্যমে startup order নির্ধারণ করে। restart policy দিয়ে এই নির্ভরতা প্রকাশ করা যায় না।
পরবর্তী reboot-এ stack আবার চালু না হয়ে স্থায়ীভাবে বন্ধ করব কীভাবে?
unless-stopped ব্যবহার করলে docker compose stop যথেষ্ট। কারণ হাতে বন্ধ করা container daemon আবার চালু হলে পুনরায় শুরু হয় না। always ব্যবহার করলে শুধু stop করা যথেষ্ট নয়; reboot-এর পরে container আবার চালু হবে। হয় docker compose down চালান, যা container-গুলো সরিয়ে দেয়, অথবা আগে docker update --restart no my-container দিয়ে policy পরিবর্তন করুন। systemd unit যদি stack পরিচালনা করে, তাহলে sudo systemctl disable myapp.service-ও চালান। তা না হলে unit আবার stack চালু করবে।