SSD Nodes Learn Hosting plans →
راهنماها Matt Connorتوسط Matt Connor · به‌روزرسانی شده 2026-10-20

راهنمای سریع Docker Compose برای سرور واقعی

دستورات روزمره Docker Compose را بر اساس کار دسته‌بندی کنید: چرخه حیات، اعمال تغییرات، لاگ، shell، شبکه، volume و پاک‌سازی ایمن، با نکته Compose V2.

دستورات Compose که در عمل بیشتر به آن‌ها نیاز پیدا می‌کنید

Docker Compose بیش از 40 زیردستور دارد. کار روزمره روی یک سرور معمولاً به حدود 12 مورد از آن‌ها نیاز دارد. این صفحه این دستورات را بر اساس کاری که انجام می‌دهید دسته‌بندی می‌کند، برای هرکدام یک دلیل روشن ارائه می‌دهد و زمانی که دستوری نکته‌ای مهم دارد، شما را به توضیح عمیق‌تر هدایت می‌کند. اگر هنوز مطمئن نیستید که اصلاً برای سرور خود به Compose نیاز دارید، تعریف ساده Docker Compose و مواردی که به آن نیازی ندارید پیش از این فهرست به همین پرسش پاسخ می‌دهد.

تمام مطالب اینجا از Compose V2 استفاده می‌کنند: docker compose با یک فاصله، نه اسکریپت قدیمی docker-compose. V2 یک افزونهٔ Go است که همراه با Docker Engine نصب می‌شود و V1 در بسته‌های فعلی دیگر ارائه نمی‌شود؛ بنابراین مشاهدهٔ docker-compose: command not found روی یک سیستم Ubuntu تازه در July 2026 رفتاری مورد انتظار است و نشان‌دهندهٔ خرابی نیست. با docker compose version بررسی کنید. اگر این دستور چیزی چاپ نکرد، بستهٔ docker-compose-plugin را نصب کنید.

تمام دستورات زیر باید از دایرکتوری‌ای اجرا شوند که compose.yaml شما را در خود دارد، زیرا Compose نام پروژه را از همان دایرکتوری می‌گیرد و فایل را نسبت به آن پیدا می‌کند. اگر همان دستور را یک سطح بالاتر اجرا کنید، Compose با no configuration file provided: not found متوقف می‌شود. اگر قالب فایل برایتان جدید است، با نخستین فایل Compose روی یک VPS شروع کنید و سپس برای آشنایی با دستورات به اینجا برگردید.

چرخهٔ حیات: چهار فرمانی که وارد می‌کنید و فرمانی که containerها را حذف می‌کند

docker compose up -d
docker compose up -d --wait
docker compose stop
docker compose start
docker compose restart web
docker compose down

up -d شبکه را ایجاد می‌کند، containerها را ایجاد می‌کند، آن‌ها را اجرا می‌کند و بازمی‌گردد. این فرمان به‌محض ایجاد شدن containerها بازمی‌گردد؛ به همین دلیل، اسکریپت deploy که پس از آن یک probe از نوع curl اجرا می‌کند، اغلب در اولین تلاش شکست می‌خورد. up -d --wait تا زمانی که هر service دارای healthcheck، وضعیت healthy را گزارش کند، منتظر می‌ماند و اگر یکی از آن‌ها هرگز به این وضعیت نرسد، با کد خروجی غیرصفر خاتمه می‌یابد. عملکرد این flag فقط به check پشت آن وابسته است؛ بنابراین پیش از آن‌که در automation به آن تکیه کنید، یک healthcheck بنویسید که Compose بتواند به آن اعتماد کند.

stop containerها را متوقف می‌کند و آن‌ها را نگه می‌دارد؛ بنابراین start همان containerها را با همان writable layer دوباره اجرا می‌کند. down آن‌ها را متوقف می‌کند و سپس containerها و network پروژه را حذف می‌کند. هر چیزی که داخل container و خارج از volume نوشته شده باشد، همراه آن‌ها حذف می‌شود. این رایج‌ترین سوءبرداشت پرهزینه در Compose است و تفاوت کامل بین down و stop توضیح می‌دهد که این مسئله در کجا مشکل‌ساز می‌شود.

restart reload نیست. این فرمان همان container را با configuration موجود در آن متوقف و دوباره اجرا می‌کند؛ بنابراین تغییر environment variable، tag جدید image یا ویرایش port mapping هیچ اثری ندارد. برای اعمال تغییر فایل، دوباره up -d را اجرا کنید. Compose هر service را با container در حال اجرای آن مقایسه می‌کند و فقط containerهایی را دوباره ایجاد می‌کند که configuration آن‌ها تغییر کرده باشد.

اعمال تغییرات: بازسازی (recreate)، دریافت (pull) یا ساخت مجدد (rebuild)

docker compose up -d --force-recreate
docker compose pull && docker compose up -d
docker compose build --no-cache web
docker compose up -d --build web

up -d به‌تنهایی وقتی تغییری ایجاد نشده باشد، کاری انجام نمی‌دهد؛ همین ویژگی باعث می‌شود اجرای مکرر آن ایمن باشد. --force-recreate این مقایسه را نادیده می‌گیرد و تمام کانتینرها را حتی در صورت یکسان بودن پیکربندی جایگزین می‌کند، بنابراین سریع‌ترین راه برای پاک‌سازی وضعیت‌های غیرعادی درون کانتینر است.

به‌روزرسانی یک ایمیج به دو دستور نیاز دارد زیرا دو کار متفاوت انجام می‌دهند. pull ایمیج فعلی را برای هر تگ ذکر شده در فایل دانلود می‌کند. سپس up -d متوجه می‌شود که شناسه ایمیج سرویس با کانتینر در حال اجرای آن مطابقت ندارد و آن را بازسازی می‌کند. اگر مرحله pull را نادیده بگیرید، up -d همان نسخه ماه گذشته latest را بدون هیچ خطایی اجرا نگه می‌دارد. ریسک معکوس در استک‌های چندسرویسی نمایان می‌شود، جایی که دریافت latest برای همه سرویس‌ها به‌طور هم‌زمان می‌تواند برنامه‌ای را که ده ثانیه قبل کار می‌کرد، از کار بیندازد؛ به همین دلیل است که یک فضای کاری AFFiNE خودمیزبان هر یک از چهار تگ ایمیج خود را ثابت (pin) می‌کند. ثابت کردن تگ‌ها همچنین باعث می‌شود ارتقا به یک ویرایش آگاهانه از تگ و سپس همان عملیات pull و recreate تبدیل شود. در استکی که دیتابیس خود را هنگام بوت مهاجرت می‌دهد، پیش از اجرای هر یک از این دو دستور باید یک dump در اختیار داشته باشید؛ این همان روالی است که یک میز پشتیبانی Chatwoot خودمیزبان برای هر ارتقای نسخه دنبال می‌کند.

build برای سرویس‌هایی اعمال می‌شود که به‌جای image:، بخش build: را تعریف کرده‌اند. up -d --build در یک مرحله عملیات ساخت و اجرا را انجام می‌دهد که حلقه معمول هنگام تغییر کد است. تنها زمانی از --no-cache استفاده کنید که یک لایه کش‌شده به‌وضوح قدیمی شده باشد، زیرا تمام لایه‌ها را از ابتدا بازسازی می‌کند. برخی پروژه‌ها هر دو مسیر را از یک مخزن کلون‌شده ارائه می‌دهند و در آن صورت، این دو حلقه مسیرهای به‌روزرسانی جایگزین هستند: یک ردیاب تمرین openGym خودمیزبان با استفاده از git pull، docker compose pull و docker compose up -d بر اساس ایمیج‌های از پیش ساخته‌شده به‌روزرسانی می‌شود و تنها زمانی که قصد ساخت از سورس را داشته باشید، از git pull و سپس docker compose up -d --build استفاده می‌کند.

مشاهده سرویس‌های در حال اجرا

docker compose ps
docker compose ps -a
docker compose logs -f --tail=100
docker compose logs --since 15m --timestamps db
docker compose top
docker compose ls

ps فقط containerهای در حال اجرا را فهرست می‌کند. سرویسی که هنگام راه‌اندازی crash کرده است، تا زمانی که -a را اضافه نکنید در این فهرست دیده نمی‌شود؛ بنابراین containerای که در ps وجود ندارد، اما ps -a آن را با وضعیت Exited (1) نشان می‌دهد، الگوی معمول یک خطای راه‌اندازی است. ابتدا exit code را بخوانید، سپس لاگ‌ها را بررسی کنید.

logs -f همه سرویس‌ها را هم‌زمان دنبال می‌کند و ابتدای هر خط نام سرویس را می‌نویسد. این همان نمایی است که هنگام ارتباط سرویس‌ها با یکدیگر و اهمیت ترتیب رویدادها به آن نیاز دارید. برای محدودکردن خروجی، نام یک سرویس را مشخص کنید. --tail=100 برای containerای که یک ماه است در حال اجراست اهمیت دارد، زیرا خروجی پیش‌فرض کل سابقه را چاپ می‌کند و ترمینال را پر می‌کند. --since 15m به پرسشی پاسخ می‌دهد که معمولاً مطرح می‌کنید: هنگام restartای که همین حالا انجام دادید چه اتفاقی افتاد؟

top پردازش‌های داخل هر container را فهرست می‌کند. این دستور تفاوت بین «container در حال اجراست» و «پردازش داخل آن در حال اجراست» را مشخص می‌کند. ls از دایرکتوری فعلی خارج می‌شود و همه پروژه‌های Compose موجود روی host را همراه با وضعیت آن‌ها فهرست می‌کند؛ بنابراین می‌توانید stackای را پیدا کنید که 3 ماه پیش راه‌اندازی کرده‌اید.

دریافت shell درون یک سرویس

docker compose exec web sh
docker compose exec -u root web sh
docker compose run --rm web env
docker compose run --rm --no-deps web sh

exec یک فرمان را داخل containerای اجراشده اجرا می‌کند. run یک container جدید را بر اساس همان تعریف سرویس راه‌اندازی می‌کند؛ این گزینه زمانی لازم است که سرویس آن‌قدر پایدار نمی‌ماند تا بتوانید با exec وارد آن شوید. همیشه run را همراه با --rm به‌کار ببرید، زیرا بدون آن، هر بار اجرا یک container متوقف‌شده باقی می‌گذارد و این containerها به‌تدریج جمع می‌شوند تا زمانی که docker compose ps -a دیگر قابل‌خواندن نباشد.

پیش از bash، sh را امتحان کنید. imageهای مبتنی بر Alpine هیچ bashای ندارند و خطا به‌شکل exec: "bash": executable file not found in $PATH نمایش داده می‌شود. افزودن --no-deps به run وابستگی‌های سرویس را نادیده می‌گیرد و مانع می‌شود یک بررسی سریع configuration، کل database شما را راه‌اندازی کند.

run --rm web env سریع‌ترین روش برای مشاهده environment واقعی دریافت‌شده توسط سرویس است؛ پس از آن‌که هر فایل .env، بلوک environment: و متغیر shell با یکدیگر ادغام شده‌اند. وقتی یک مقدار نادرست است، معمولاً ترتیب ادغام علت آن است و نحوه resolve کردن env fileها و secretها توسط Compose مشخص می‌کند کدام منبع اولویت دارد.

شبکه‌ها، پورت‌ها و حل نام

docker compose port web 80
docker compose exec web getent hosts db
docker compose config --networks

Compose همه سرویس‌ها را روی یک شبکهٔ پروژه قرار می‌دهد و نام هر سرویس در آن شبکه یک نام DNS است. اجرای getent hosts db درون web، در صورت موفقیت حل نام، IP کانتینر را چاپ می‌کند و در صورت شکست چیزی چاپ نمی‌کند؛ بنابراین در عرض 2 ثانیه مشخص می‌کند که «آیا این کانتینرها می‌توانند یکدیگر را ببینند یا نه». اگر نام resolve شود اما اتصال رد شود، فرایند داخل db به‌جای 0.0.0.0 روی 127.0.0.1 bind شده است؛ بنابراین هیچ packetی را از کانتینر دیگری نمی‌پذیرد. همین مرزبندی توضیح می‌دهد که چرا کانتینری که خارج از پروژه اجرا شده است، چه با docker run و چه به‌عنوان stack مستقل، اصلاً نمی‌تواند نامی مانند jellyfin را resolve کند. وقتی یک front end از Halcyon برای library جلیفین شما نمی‌تواند به سروری که به آن اشاره می‌کند دسترسی پیدا کند، این نخستین موردی است که باید بررسی شود. جزئیات باقی‌ماندهٔ این مدل در نحوهٔ کار شبکه‌های Compose و DNS سرویس‌ها آمده است.

port web 80 آدرس host و پورتی را چاپ می‌کند که پورت کانتینر روی آن publish شده است. این کار زمانی که mapping از یک variable آمده باشد، از حدس‌زدن جلوگیری می‌کند. Publish کردن یک پورت همچنین یک firewall rule ایجاد می‌کند که Docker خودش آن را مدیریت می‌کند. این rule پیش از ruleهای شما اعمال می‌شود؛ بنابراین سرویسی که تصور می‌کردید private است ممکن است در اینترنت باز باشد. این وضعیت در چرا پورت‌های publish‌شدهٔ Docker از ufw عبور می‌کنند توضیح داده شده است. روش امن‌تر این است که این پورت‌ها را publish نکنید و در عوض یک proxy دارای احراز هویت را روی شبکهٔ پروژه، در برابر سرویس‌ها قرار دهید. اجرای Authentik به‌عنوان لایهٔ single sign-on همین الگو را فراهم می‌کند.

Volumeها و داده‌ها

docker compose config --volumes
docker compose cp db:/etc/postgresql/pg_hba.conf ./pg_hba.conf
docker compose down -v

config --volumes، volumeهای نام‌گذاری‌شده‌ای را که پروژه تعریف کرده است، هرکدام در یک خط نمایش می‌دهد. همین فهرست، مبنای تهیه نسخه پشتیبان است. وقتی volumeها داده‌های غیرقابل‌جایگزین دارند، دستور دقیق تهیه نسخه پشتیبان به‌اندازه خود فهرست اهمیت دارد؛ به همین دلیل مقایسه PhotoPrism و Immich دستورهای dump و copy موردنیاز هر photo server را به‌صراحت ارائه می‌کند. cp بدون باز کردن shell، یک فایل را به container کپی می‌کند یا از آن خارج می‌کند و از قالب service:path در سمتی استفاده می‌کند که container قرار دارد.

down -v این volumeهای نام‌گذاری‌شده را همراه با containerها حذف می‌کند. این دستور برای جمع‌کردن یک stack آزمایشی مناسب است، اما برای هر چیزی که داده مهمی در آن نگهداری می‌شود مناسب نیست؛ زیرا هیچ تأییدی نمی‌گیرد و امکان بازگردانی ندارد. Bind mountها تحت تأثیر این دستور قرار نمی‌گیرند، چون روی filesystem میزبان قرار دارند. همین تفاوت در دامنه آسیب، یکی از دلایل انتخاب آگاهانه میان bind mountها و volumeهای نام‌گذاری‌شده است.

پاک‌سازی فضای دیسک بدون از دست دادن داده‌ها

docker compose down --remove-orphans
docker system df
docker image prune -a
docker builder prune

--remove-orphans کانتینرهایی را که متعلق به پروژه هستند اما دیگر در فایل تعریف نشده‌اند حذف می‌کند؛ این دقیقاً همان وضعیتی است که پس از تغییر نام یک سرویس رخ می‌دهد. بدون این دستور، آن کانتینرها به اجرا ادامه می‌دهند و برای docker compose ps غیرقابل مشاهده باقی می‌مانند.

docker system df پیش از حذف هر چیزی، نشان می‌دهد که فضای دیسک کجا مصرف شده است و تفکیک دقیقی از ایمیج‌ها، کانتینرها، ولوم‌های محلی و کش ساخت (build cache) به همراه مقدار فضای قابل بازیابی برای هر کدام ارائه می‌دهد. image prune -a تمام ایمیج‌هایی را که توسط هیچ کانتینری استفاده نمی‌شوند (چه تگ‌دار و چه بدون تگ) حذف می‌کند. در سروری که نسخه‌های متعددی از یک ایمیج حجیم را دریافت کرده است، این دستور معمولاً بیشترین فضای آزاد را ایجاد می‌کند. builder prune کش ساخت را پاک می‌کند؛ این کش در هر سروری که ایمیج‌های خود را می‌سازد، به‌مرور زمان به‌طور نامحسوس رشد می‌کند.

هیچ‌کدام از این دستورات به ولوم‌های نام‌گذاری‌شده (named volumes) دست نمی‌زنند. تنها docker volume prune --all و docker compose down -v این کار را انجام می‌دهند. دستور ساده docker volume prune فقط ولوم‌های ناشناس (anonymous volumes) را حذف می‌کند.

بررسی فایل پیش از ایجاد مشکل

docker compose config --quiet
docker compose config --services
docker compose --dry-run up -d

config --quiet فایل را اعتبارسنجی می‌کند و در صورت موفقیت چیزی چاپ نمی‌کند؛ بنابراین باید در مرحلهٔ پیش از استقرار یا یک git hook استفاده شود. config ساده، فایل کاملاً ادغام‌شده و جایگزین‌شده را چاپ می‌کند. با این خروجی می‌توانید بررسی کنید که یک متغیر به‌درستی resolve شده و فایل override مطابق انتظار روی فایل اصلی اعمال شده است. متغیر unset در این خروجی به‌صورت مقداری خالی و در کنار هشدار The "X" variable is not set. Defaulting to a blank string. ظاهر می‌شود.

--dry-run یک global flag است، نه flag مربوط به subcommand؛ بنابراین باید پیش از up قرار بگیرد. این گزینه تمام اقدام‌هایی را که Compose انجام خواهد داد چاپ می‌کند و هیچ تغییری ایجاد نمی‌کند. صرف 30 ثانیه برای این بررسی، پیش از اجرای down روی stackهای مهم، ارزش دارد.

کار با فایل‌ها، profileها و پروژه‌ها

docker compose -f compose.yaml -f compose.prod.yaml up -d
docker compose --profile debug up -d
docker compose -p staging up -d

چند -f به‌ترتیب با یکدیگر ادغام می‌شوند و فایل‌های بعدی، هر کلید را به‌صورت جداگانه روی فایل‌های قبلی override می‌کنند. این روش استانداردی برای نگه‌داشتن یک فایل پایه و یک override کوچک برای production است؛ اما قوانین ادغام برای listها و mapها متفاوت است. بنابراین پیش از عیب‌یابی یک نتیجه غیرمنتظره، نحوه ادغام چند فایل Compose را بخوانید.

--profile سرویس‌هایی را که با آن profile برچسب‌گذاری شده‌اند، در کنار سرویس‌های بدون برچسب profile اجرا می‌کند. به این ترتیب، ابزارهای debug وارد یک up معمولی نمی‌شوند. -p نام پروژه را تعیین می‌کند؛ بنابراین دو کپی از یک stack می‌توانند هم‌زمان با networkها و نام‌های volume جداگانه اجرا شوند. بازیابی stack پس از reboot با فرمانی که شما مستقیماً وارد می‌کنید انجام نمی‌شود؛ بلکه unitای اجرا می‌شود که این کار را برای شما انجام می‌دهد. توضیحات آن در راه‌اندازی stackهای Compose هنگام boot آمده است.

FAQ

چه چیزی جایگزین docker-compose با خط تیره شد؟

نسخه Compose V2 که با docker compose و با یک فاصله فراخوانی می‌شود. این نسخه یک پلاگین است که همراه با Docker Engine ارائه می‌شود و ابزار پایتونی V1 دیگر در بسته‌های فعلی نصب نمی‌شود. اگر دستور با فاصله خروجی نداشت، بسته docker-compose-plugin را برای توزیع سیستم‌عامل خود نصب کنید. اسکریپت‌های قدیمی را به فرمت جدید (با فاصله) به‌روزرسانی کنید و از ایجاد alias خودداری کنید، زیرا V2 دارای فلگ‌هایی است که در V1 وجود نداشتند.

چرا docker compose restart تغییرات پیکربندی من را اعمال نمی‌کند؟

دستور restart کانتینر موجود را با همان پیکربندی اولیه متوقف و دوباره اجرا می‌کند و هرگز فایل compose.yaml را مجدداً نمی‌خواند. هر تغییری در متغیرهای محیطی، پورت‌ها، ولوم‌ها یا تگ ایمیج نیازمند اجرای docker compose up -d است که هر سرویس را با کانتینر در حال اجرای آن مقایسه کرده و موارد متفاوت را بازسازی می‌کند. اگر می‌خواهید جایگزینی انجام شود حتی با وجود اینکه تغییری در فایل ایجاد نشده است، از --force-recreate استفاده کنید.

چگونه یک سرویس را به ایمیج جدیدتر به‌روزرسانی کنم؟

ابتدا docker compose pull و سپس docker compose up -d را اجرا کنید. دستور pull آخرین ایمیج مربوط به هر تگ در فایل را دریافت می‌کند و up -d هر سرویسی که ID ایمیج آن با کانتینر در حال اجرا مطابقت نداشته باشد را بازسازی می‌کند. اجرای up -d به تنهایی، از ایمیجی که قبلاً روی دیسک موجود است استفاده می‌کند؛ به همین دلیل است که یک stack که روی latest قفل شده است، ماه‌ها بدون دریافت خطا از یک بیلد قدیمی استفاده می‌کند.

کدام دستورات پاک‌سازی روی یک سرور فعال ایمن هستند؟

دستورات docker system df، docker image prune -a و docker builder prune فقط ایمیج‌ها و کش را حذف می‌کنند، بنابراین سرویس‌های در حال اجرا به کار خود ادامه می‌دهند و ولوم‌های نام‌گذاری‌شده (named volumes) دست‌نخورده باقی می‌مانند. جفت خطرناک، دستور docker compose down -v است که ولوم‌های نام‌گذاری‌شده پروژه را بدون پرسش حذف می‌کند، و docker volume prune --all که تمام ولوم‌های نام‌گذاری‌شده‌ای که توسط هیچ کانتینری استفاده نمی‌شوند را پس از یک تایید y/N یا بدون هیچ پرسشی در صورت استفاده از -f حذف می‌کند. ابتدا docker compose config --volumes را اجرا کنید تا بدانید چه چیزی در معرض خطر است.

آیا می‌توانم یک دستور را بدون اجرای کل stack اجرا کنم؟

بله. دستور docker compose run --rm --no-deps web sh یک کانتینر تکی را از تعریف سرویس web اجرا می‌کند، وابستگی‌های آن را نادیده می‌گیرد و پس از خروج شما، کانتینر را حذف می‌کند. زمانی که کانتینر از قبل در حال اجراست از exec استفاده کنید، زیرا exec به پروسه در حال اجرا متصل می‌شود و وضعیت واقعی سرویس را به شما نشان می‌دهد.

آیا این مقاله مفید بود؟