تفاوت docker compose stop و down چیست؟
تفاوت اصلی در حذف کانتینرها و شبکهها است. دستور stop فقط کانتینر را متوقف میکند اما down آن را حذف میکند. هیچکدام دادههای volume را بدون پرچم v- پاک نمیکنند.
پاسخ کوتاه
docker compose stop کانتینرها را متوقف میکند و آنها را روی دیسک باقی میگذارد. docker compose down کانتینرها را متوقف میکند و سپس کانتینرها و شبکهای را که Compose برای پروژه ایجاد کرده است حذف میکند. هیچیک از این فرمانها روی volume نامگذاریشده تأثیر نمیگذارند. پایگاه داده فقط زمانی حذف میشود که -v را اضافه کنید؛ مانند docker compose down -v که volumeهای نامگذاریشده اعلامشده در بخش volumes فایل Compose را حذف میکند.
تمام تفاوت در همین یک پاراگراف خلاصه میشود. در ادامه این راهنما، این تفاوت با یک volume مربوط به Postgres اثبات میشود؛ میتوانید باقیماندن آن پس از down و حذفشدن آن در اثر down -v را مشاهده کنید. همچنین دو موردی را توضیح میدهد که در آنها به --force-recreate نیاز دارید.
دستور docker compose stop: کانتینرها باقی میمانند
stop سیگنال SIGTERM را به پردازش اصلی در هر کانتینر ارسال میکند، منتظر میماند و اگر پردازش همچنان فعال باشد، SIGKILL را میفرستد. زمان انتظار پیشفرض 10 ثانیه است و -t آن را تغییر میدهد. هیچ چیزی حذف نمیشود. کانتینر شناسه (ID)، لایه قابلنوشتن، رزرو IP و لاگهای خود را حفظ میکند.
docker compose stop
docker compose ps -adocker compose ps بهتنهایی فقط کانتینرهای در حال اجرا را نشان میدهد، بنابراین پس از stop یک جدول خالی چاپ میکند و کاربران تصور میکنند کانتینرها از بین رفتهاند. ps -a کانتینرهای متوقفشده را نیز شامل میشود و در آنجا وضعیت Exited را در کنار هر سرویس، همراه با کد خروجی پردازش آن مشاهده خواهید کرد. یک خاموشی تمیز وضعیت Exited (0) را نشان میدهد. کد غیر صفر به این معنی است که پردازش با شکست مواجه شده یا پس از اتمام زمان انتظار، کشته شده است. آنها را با docker compose start دوباره بالا بیاورید؛ این دستور دقیقاً از همان کانتینرهای قبلی استفاده میکند.
از آنجا که کانتینرها همچنان وجود دارند، هر چیزی که داخل آنها و خارج از یک volume نوشته شده باشد، باقی میماند. این شامل بستهای که بهصورت دستی با docker compose exec نصب کردهاید و فایل پیکربندی که داخل کانتینر ویرایش کردهاید نیز میشود. این دلیل عملی برای ترجیح دادن stop در حین دیباگ کردن است: شما میتوانید با همان وضعیت قبلی، سرویس را دوباره راهاندازی کنید.
docker compose down: کانتینرها و شبکهها حذف میشوند
down کانتینرها را متوقف میکند و سپس آنها را همراه با شبکه پیشفرضی که Compose برای پروژه ایجاد کرده است، حذف میکند. مستندات Docker این فرمان را بهصورت متوقفکردن کانتینرها و حذف کانتینرها، شبکهها، volumeها و imageهایی توصیف میکند که up ایجاد کرده است؛ اما حذف volumeها و imageها فقط زمانی انجام میشود که آنها را با -v و --rmi درخواست کنید.
docker compose down
docker compose ps -a
docker network lsپس از اجرای down، ps -a برای پروژه چیزی نمایش نمیدهد و شبکه <project>_default نیز حذف شده است. نام پروژه از نام پوشه گرفته میشود، مگر اینکه name: را در فایل Compose تنظیم کنید یا -p را ارسال کنید. هر تغییری که داخل لایه قابلنوشتن یک کانتینر ایجاد کردهاید، اکنون غیرقابلبازیابی است. بنابراین down را فرمانی در نظر بگیرید که کانتینر را کنار میگذارد و دادههایی را که در volumeها قرار دادهاید حفظ میکند.
اگر این فرمان را در پوشه اشتباه اجرا کنید، no configuration file provided: not found را دریافت میکنید. Compose نمیداند منظور شما کدام پروژه است و در نتیجه از ادامه کار خودداری میکند. وقتی در پوشه پروژه نیستید، از docker compose -f /srv/myapp/compose.yaml down استفاده کنید.
آیا دستور docker compose down حجمهای (volumes) من را حذف میکند؟
خیر. یک volume نامگذاریشده که در سطح بالای فایل تحت کلید volumes تعریف شده باشد، پس از اجرای down باقی میماند و عمر آن از کانتینری که به آن متصل بوده نیز بیشتر است. این رایجترین نگرانی در مورد این دستور است و پاسخ آن از نسخه Compose v2 تا نسخههای فعلی v5 تغییری نکرده است.
یک stack برای تست ایجاد کنید. محتوای زیر را در compose.yaml در یک دایرکتوری خالی به نام voltest قرار دهید.
services:
db:
image: postgres:16.4
restart: unless-stopped
environment:
POSTGRES_PASSWORD: example
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:آن را اجرا کنید و یک ردیف داده بنویسید که بعداً بتوانید آن را تشخیص دهید.
docker compose up -d
sleep 10
docker compose exec -T db psql -U postgres -c "create table marker (note text);"
docker compose exec -T db psql -U postgres -c "insert into marker values ('survived');"حالا کانتینر را از بین ببرید و وضعیت volume را بررسی کنید.
docker compose down
docker volume lsخروجی همچنان voltest_pgdata را نمایش میدهد. کانتینر حذف شده است اما دادهها باقی ماندهاند. stack را دوباره بالا بیاورید و ردیف داده را بخوانید.
docker compose up -d
sleep 10
docker compose exec -T db psql -U postgres -c "select * from marker;"شما یک ردیف شامل survived دریافت میکنید. کانتینر جدید، یک کانتینر متفاوت با ID متفاوت است که به همان volume قبلی متصل شده است. اگر به دنبال درک جامعتری هستید، راهنمای اصول اولیه Compose تفاوت volumeهای نامگذاریشده با bind mountها و محل واقعی ذخیرهسازی هر کدام روی میزبان (host) را توضیح میدهد.
آنچه down -v دقیقاً از بین میبرد
-v (شکل کامل --volumes) volumeهای نامگذاریشدهای را که در بخش volumes فایل Compose تعریف شدهاند، بههمراه volumeهای ناشناس متصل به containerها حذف میکند. آن را روی همان stack اجرا کنید.
docker compose down -v
docker volume lsvoltest_pgdata دیگر فهرست نمیشود. stack را دوباره اجرا کنید تا entrypoint مربوط به Postgres یک data directory خالی پیدا کند و یک cluster جدید را مقداردهی اولیه کند. لاگ container این موضوع را بهصراحت نشان میدهد.
The files belonging to this database system will be owned by user "postgres".
PostgreSQL init process complete; ready for start up.دیدن این بخش در stackای که ماهها در حال اجرا بوده است، یعنی volume حذف شده است. جدول marker شما از بین رفته است و تنها راه بازگشت، استفاده از backup است.
برخی storageها هرگز با -v حذف نمیشوند. bind mount یک مسیر روی host است؛ بنابراین Docker فقط آن را unmount میکند و فایلهای شما در محل خود باقی میمانند. volumeای که با external: true علامتگذاری شده است، متعلق به چیزی خارج از این پروژه اعلام میشود و Compose هرگز آن را حذف نمیکند. named volumeای که پیش از اجرای down -v از فایل Compose حذف کردهاید، دیگر در فایل تعریف نشده است؛ بنابراین Compose از وجود آن برای حذفکردن آگاه نیست و آن volume بهعنوان orphan برای docker volume prune باقی میماند.
این حالت هنگام refactor افراد را دچار مشکل میکند. یک service و volume آن را از فایل حذف کنید و down -v را اجرا کنید؛ volume باقی میماند، چون فایل دیگر به آن اشاره نمیکند. down -v را پیش از ویرایش فایل اجرا کنید، نه پس از آن.
چه زمانی واقعاً به --force-recreate نیاز دارید
docker compose up -d هر بار همهچیز را از نو ایجاد نمیکند. Compose یک hash از پیکربندی نهایی هر service را بهعنوان label روی container ذخیره میکند. اگر hash و image ID یکسان باشند، container بدون تغییر باقی میماند و بهجای Recreated، Container voltest-db-1 Running را دریافت میکنید. این رفتار تقریباً همیشه مطلوب است، چون اجرای مکرر up -d را ایمن میکند.
به همین دلیل است که بعضی ویرایشها ظاهراً هیچ اثری ندارند. Compose تعریف نهایی service را hash میکند، نه محتوای فایلهایی را که این تعریف به آنها اشاره میکند. اگر یک config file در container mount شده باشد و فقط هنگام startup خوانده شود، ویرایش آن باعث recreate نمیشود، چون مسیر mount تغییر نکرده است. service با همان مقادیری که هنگام boot خوانده بود به اجرای خود ادامه میدهد.
docker compose up -d --force-recreateاین دستور هر container را متوقف و حذف میکند و سپس container جدیدی را بر اساس همان تعریف ایجاد میکند. پس از ویرایش یک config file که mount شده است، و همچنین زمانی که وضعیت یک container به حالتی نامشخص تغییر کرده است، از آن استفاده کنید. Volumes دستنخورده باقی میمانند؛ بنابراین database در force recreate حفظ میشود. برای دریافت image جدیدتر با همان tag، باید pull را نیز اجرا کنید.
docker compose pull
docker compose up -dpull image ID جدید را دریافت میکند و سپس up -d متوجه میشود image ID با container در حال اجرا متفاوت است و container را بهصورت خودکار recreate میکند. افزودن --force-recreate بدون pull، container جدیدی را از همان image قدیمی ایجاد میکند. به همین دلیل، شکایت «force recreate کردم، اما هنوز همان نسخه قدیمی است» بسیار رایج است.
docker compose restart هیچیک از این کارها را انجام نمیدهد. این دستور containerهای موجود را restart میکند و اصلاً فایل Compose را دوباره نمیخواند؛ بنابراین تغییر environment variable یا تغییر port mapping اعمال نخواهد شد. اگر فایل را ویرایش کردهاید، از up -d استفاده کنید.
مدل ذهنی برای بهخاطر سپردن
کانتینرها قابل جایگزینی هستند. یک کانتینر، فرآیندی به همراه یک لایه نازک قابلنوشتن است و Compose میتواند یک کانتینر مشابه را از روی فایل مربوطه در حدود یک ثانیه بسازد. Volumeها قابل جایگزینی نیستند، زیرا تنها نسخه از وضعیت (state) را در خود نگه میدارند که هیچ فایلی در مخزن (repository) شما قادر به بازسازی آن نیست.
هر فعل در Compose با این تفکیک همخوانی دارد. stop و start کانتینر را حفظ میکنند. down و up کانتینر را جایگزین کرده و Volume را حفظ میکنند. down -v تنها دستور روتینی است که وضعیت را حذف میکند و به همین دلیل به یک فلگ صریح نیاز دارد. پیش از اجرای آن روی هر محیط واقعی، اطمینان حاصل کنید که یک نسخه پشتیبان دارید که حداقل یک بار آن را بازیابی کردهاید.
همین منطق در مورد secrets نیز صدق میکند. رمز عبوری که از طریق POSTGRES_PASSWORD تنظیم میشود، تنها در اولین باری که دیتابیس مقداردهی اولیه (initialize) میشود خوانده میشود؛ بنابراین تغییر آن در فایل environment و اجرای up -d منجر به password authentication failed for user "postgres" میشود. کانتینر جدید است اما Volume قدیمی است و Volume قدیمی همچنان رمز عبور قبلی را در خود دارد. نحوه حلوفصل فایلهای env و secrets توسط Compose توضیح میدهد که وقتی یک متغیر دو بار تنظیم شود، کدام لایه اولویت دارد.
حالتهای شکست و پیامهایی که مشاهده خواهید کرد
no configuration file provided: not found به این معناست که Compose در دایرکتوری در حال اجراست که فاقد compose.yaml و docker-compose.yml است. از -f با مسیر کامل استفاده کنید.
network voltest_default has active endpoints روی down به این معناست که یک کانتینر خارج از این پروژه به شبکهٔ پروژه متصل شده است؛ معمولاً کانتینری که بهصورت دستی با docker run --network راهاندازی شده باشد. آن کانتینر را حذف کنید و سپس دوباره down را اجرا کنید.
Found orphan containers (voltest-old-1) for this project پس از تغییر نام یا حذف یک سرویس ظاهر میشود. نسخهٔ فعلی Compose نامها را بهصورت ساده و با کاما از هم جدا میکند. نسخههای قدیمیتر Compose v2 همین هشدار را با نامهایی در براکت چاپ میکردند، مانند ([voltest-old-1]). کانتینر قدیمی همچنان برچسب پروژه را حمل میکند. docker compose down --remove-orphans آنها را پاکسازی میکند و اجرای آن روی یک stack سالم، ایمن است.
Error response from daemon: remove voltest_pgdata: volume is in use در یک docker volume rm دستی به این معناست که هنوز کانتینری به این volume ارجاع میدهد، حتی اگر کانتینر متوقف شده باشد. ابتدا docker compose down را اجرا کنید و سپس volume را حذف کنید، یا بهسادگی از down -v استفاده کنید. در پروژههای بزرگتر، یک stack چند سرویسی Compose نشان میدهد که یک پروژه تا چه حد میتواند volume انباشته کند.
FAQ
آیا down پایگاه داده من را حذف میکند؟
اگر پایگاه داده در یک volume نامگذاریشده یا bind mount قرار داشته باشد، حذف نمیشود. down کانتینرها و network پروژه را حذف میکند، اما volume همراه با دادههای سالم روی دیسک باقی میماند. اجرای بعدی docker compose up -d یک کانتینر جدید را به همان volume متصل میکند و دادهها در دسترس هستند. فقط docker compose down -v volumeهای نامگذاریشده را حذف میکند؛ آن هم فقط volumeهایی را که در بخش volumes فایل Compose تعریف شدهاند.
تفاوت stop و down برای کانتینری که میخواهم دوباره از آن استفاده کنم چیست؟
stop کانتینر را حفظ میکند؛ بنابراین docker compose start شما را به همان کانتینر، با همان لایه قابلنوشتن، بازمیگرداند. هر چیزی که بهصورت دستی داخل کانتینر نصب یا ویرایش کرده باشید، همچنان باقی میماند. down کانتینر را حذف میکند؛ بنابراین اجرای بعدی up -d یک کانتینر جدید را از image ایجاد میکند و تغییرات دستی از بین میروند. هنگام عیبیابی از stop استفاده کنید.
چگونه همه چیزهایی را که یک پروژه Compose ایجاد کرده است حذف کنم؟
docker compose down -v --rmi all --remove-orphans کانتینرها، network پروژه، volumeهای نامگذاریشده تعریفشده در فایل، imageهای استفادهشده توسط سرویسها و هر کانتینری را که هنوز با نام پروژه برچسبگذاری شده باشد حذف میکند. این دستور به bind mountها یا volumeهای علامتگذاریشده با external: true دست نمیزند. پیش از اجرای آن، با docker volume ls مواردی را که قرار است از دست بدهید بررسی کنید.
چرا کانتینر من تغییری را که در فایل پیکربندی mountشده ایجاد کردهام نادیده میگیرد؟
Compose برای تصمیمگیری درباره ایجاد دوباره کانتینر، hash مربوط به تعریف resolveشده سرویس را مقایسه میکند. محتوای فایل mountشده در این hash لحاظ نمیشود. چون مسیر تغییر نکرده است، Compose کانتینر را در حال اجرا نگه میدارد و همان مقادیری را استفاده میکند که هنگام راهاندازی خوانده بود. docker compose up -d --force-recreate را اجرا کنید تا کانتینر جدیدی ایجاد شود که فایل را دوباره بخواند.
چرا POSTGRES_PASSWORD جدید من پس از تغییر، کار نمیکند؟
image مربوط به Postgres فقط هنگام مقداردهی اولیه یک data directory خالی، POSTGRES_PASSWORD را میخواند. volume شما از قبل یک cluster مقداردهیشده دارد؛ بنابراین این متغیر نادیده گرفته میشود و رمزعبور قبلی همچنان معتبر است. password authentication failed for user "postgres" را مشاهده خواهید کرد. رمزعبور را با ALTER USER در پایگاه داده در حال اجرا تغییر دهید، یا اگر پذیرش از دست رفتن دادهها برایتان قابلقبول است، با docker compose down -v از ابتدا شروع کنید.