دیسک سرور پر شده و حتی نمی‌توانی وارد شوی؛ پیدا کردن مشکل

دیسک سرور پر شده و حتی نمی‌توانی وارد شوی؛ پیدا کردن مشکل

ساعت دو نصف شب است. سایت ۵۰۰ می‌دهد، دیتابیس بالا نمی‌آید، و وقتی ssh می‌زنی یا اصلاً وصل نمی‌شوی یا به محض ورود پیام No space left on device می‌گیری. حتی vim باز نمی‌شود چون نمی‌تواند فایل موقتش را بنویسد. دیسک پر است، و پر شدن دیسک همه‌چیز را با هم می‌شکند: لاگ‌نوشتن، سشن، سوکت، همه به فضای خالی نیاز دارند.

این راهنما ترتیبی را می‌دهد که واقعاً جواب می‌دهد. اول از کنسول کمی جا باز می‌کنی تا نفس بکشی، بعد مقصر واقعی را پیدا می‌کنی، و آخرش کاری می‌کنی که ساعت دو نصف شب دیگر تکرار نشود.

اول: وقتی SSH اصلاً بالا نمی‌آید، از کنسول جا باز کن

اگر ورود SSH ممکن نیست، از کنسول وب پنل مجازی‌سازت (VNC یا KVM) وارد شو. همین که یک ترمینال داشته باشی، هدف فقط آزاد کردن چند صد مگابایت است تا سرویس‌ها دوباره بالا بیایند و بتوانی درست تشخیص بدهی.

سریع‌ترین برنده‌ها، بدون ریسک از دست رفتن داده:

  1. sudo journalctl --vacuum-size=200M — لاگ‌های قدیمی systemd را می‌برد. اغلب همین چند گیگ آزاد می‌کند.
  2. sudo apt-get clean (یا dnf clean all) — کش بسته‌های دانلودشده را پاک می‌کند.
  3. یک لاگ بزرگ را خالی کن بدون حذف فایل: sudo truncate -s 0 /var/log/nginx/access.log. توجه کن truncate بزن، نه rm؛ چون پروسه‌ای که فایل را باز نگه داشته، با rm فضا را پس نمی‌دهد.

حالا که چند مگابایت داری، سرویس‌ها را بالا بیاور و برو سراغ تشخیص واقعی.

df -h در برابر df -i: دو مشکل که یک شکل دارند

قبل از هر پاک‌سازی، دو دستور بزن. این دو، دو خطای کاملاً متفاوت را از هم جدا می‌کنند که هر دو دقیقاً پیام No space left on device می‌دهند:

  1. df -h — درصد اشغال بلاک‌های دیسک را نشان می‌دهد.
  2. df -i — درصد اشغال inodeها را نشان می‌دهد.

اگر ستون Use% در df -h روی ۱۰۰٪ است، مشکل حجم است و باید فایل بزرگ پیدا کنی. اما اگر df -h فضای خالی نشان می‌دهد و در عوض IUse% در df -i روی ۱۰۰٪ است، دیسک از نظر حجم خالی است ولی inodeها تمام شده‌اند. این یعنی تعداد بی‌شماری فایل ریز داری، نه چند فایل بزرگ. مقصر معمول: صف ایمیل انباشته، فایل‌های سشن PHP، کش‌های ریز، یا یک اسکریپت که میلیون‌ها فایل کوچک ساخته.

راه‌حل این دو فرق دارد. برای حجم، دنبال فایل بزرگ می‌گردی. برای inode، دنبال پوشه‌ای می‌گردی که تعداد فایلش نجومی است:

sudo du --inodes -xh --max-depth=1 / | sort -rh | head

پرچم --inodes در نسخه‌های coreutils از حدود ۲۰۱۳ به بعد هست. اگر روی سیستم قدیمی کار نکرد، از این استفاده کن:

for d in /*; do echo -n "$d "; sudo find "$d" -xdev | wc -l; done

مقصر حجمی را با du پیدا کن

وقتی مطمئن شدی مشکل حجم است، از ریشه شروع کن و لایه‌به‌لایه پایین برو:

sudo du -xh --max-depth=1 / | sort -rh | head

پرچم -x مهم است؛ باعث می‌شود du روی همان فایل‌سیستم بماند و وارد /proc، /sys و ماونت‌های دیگر نشود. بزرگ‌ترین پوشه را که دیدی، همان مسیر را جای / بگذار و دوباره بزن. معمولاً دو سه بار تکرار تو را به مقصر می‌رساند: تقریباً همیشه چیزی زیر /var است.

فایلی که du نمی‌بیند ولی lsof می‌بیند

یک حالت هست که آدم را دیوانه می‌کند: df می‌گوید دیسک پر است، ولی du در کل سیستم چیزی پیدا نمی‌کند که این حجم را توضیح بدهد. اختلاف واقعی است و دلیل روشنی دارد.

du درخت پوشه‌ها را می‌پیماید و فقط فایل‌هایی را می‌شمارد که هنوز یک نام در سیستم فایل دارند. df اما بلاک‌های واقعاً اشغال‌شده را گزارش می‌دهد، از جمله بلاک‌های فایلی که پاک شده ولی هنوز باز است. وقتی یک پروسه فایلی را باز نگه داشته و کسی آن فایل را rm کرده، لینوکس فضا را تا بسته‌شدن آخرین توصیف‌گر فایل آزاد نمی‌کند. لاگی که چرخانده شده ولی سرویس هنوز به نسخه‌ی قدیمی می‌نویسد، کلاسیک‌ترین نمونه است.

برای دیدن این فایل‌ها:

sudo lsof +L1

این دستور فایل‌هایی را نشان می‌دهد که تعداد لینک‌شان صفر است (یعنی حذف شده‌اند) ولی هنوز باز مانده‌اند. ستون‌ها به تو می‌گویند کدام پروسه مقصر است و چند بایت گیر افتاده. راه‌حل، ری‌استارت همان سرویس است؛ به محض بسته شدن توصیف‌گر، فضا برمی‌گردد. اگر نمی‌خواهی سرویس را ری‌استارت کنی، می‌توانی توصیف‌گر را از /proc/PID/fd/ با truncate صفر کنی، ولی ری‌استارت تمیزتر است.

مقصرهای همیشگی و دستور پاک‌سازی هرکدام

وقتی du پوشه‌ی متهم را نشانت داد، معمولاً یکی از این‌هاست. هرکدام دستور امن خودش را دارد:

مقصرچطور مطمئن شویپاک‌سازی امن
لاگ systemd (journald)journalctl --disk-usagesudo journalctl --vacuum-time=2d
لاگ‌های چرخش‌نکرده در /var/logsudo du -xh --max-depth=1 /var/logsudo truncate -s 0 file.log و درست کردن logrotate
کرنل‌های قدیمی (پر شدن /boot)dpkg -l | grep linux-imagesudo apt-get autoremove --purge
ایمیج و والیوم داکرdocker system dfdocker system prune -af
بین‌لاگ MySQL/MariaDBحجم mysql-bin.* در دیتادایرکتوریPURGE BINARY LOGS BEFORE ... (نه rm)

چند نکته که اشتباه‌های گران را جلو می‌گیرد:

journald. به‌طور پیش‌فرض journald تا ۱۰٪ حجم فایل‌سیستم را می‌گیرد، با سقف ۴ گیگابایت (طبق مستند journald.conf در systemd). روی یک دیسک ۴۰ گیگی این یعنی تا ۴ گیگ لاگ. اگر می‌خواهی سقفش کمتر باشد، در /etc/systemd/journald.conf مقدار SystemMaxUse=500M بگذار و systemctl restart systemd-journald بزن.

داکر. docker system prune -af ایمیج‌ها و کش بیلد بلااستفاده را می‌برد و امن است. اما اگر --volumes اضافه کنی، والیوم‌های استفاده‌نشده هم پاک می‌شوند و این می‌تواند داده‌ی دیتابیس کانتینری‌ات را ببرد. قبل از --volumes مطمئن شو می‌دانی چه چیزی حذف می‌شود.

بین‌لاگ MySQL. این خطرناک‌ترین مورد است. هیچ‌وقت فایل‌های mysql-bin.* را با rm پاک نکن؛ MySQL یک ایندکس داخلی (mysql-bin.index) نگه می‌دارد و حذف دستی فایل، آن را خراب می‌کند و ریپلیکیشن را می‌شکند. اگر دیسک آن‌قدر پر است که MySQL اصلاً بالا نمی‌آید، اول چند مگ بیرون از دیتادایرکتوری آزاد کن (/tmp، لاگ‌های /var/log، کش بسته‌ها)، بگذار سرویس بالا بیاید، بعد وصل شو و PURGE BINARY LOGS BEFORE DATE_SUB(NOW(), INTERVAL 2 DAY); بزن. اگر ریپلیکا داری، اول مطمئن شو موقعیت خواندن هر ریپلیکا از این مرز گذشته باشد. برای اینکه دوباره تکرار نشود، binlog_expire_logs_seconds را ست کن تا خودکار پاک شوند.

کِی این از سمت تو قابل حل نیست

صادقانه: تقریباً همیشه پر شدن دیسک سرور مجازی مشکل سمت شماست و با همین دستورها حل می‌شود. میزبان نمی‌تواند و نباید لاگ‌ها و داده‌های شما را از طرف شما پاک کند.

دو استثنا هست. اول، وقتی سیستم فایل خودش خراب شده و df عددهای بی‌معنی می‌دهد یا ماونت read-only شده؛ آن‌جا مشکل فضا نیست و باید از حالت rescue با fsck جلو بروی. دوم، وقتی داده‌ات واقعاً بزرگ شده و دیسک به‌درستی اندازه‌گیری شده ولی دیگر جا نیست؛ هیچ دستوری این را حل نمی‌کند و باید ظرفیت را بالا ببری. اگر بار کاری‌ات به‌طور مداوم دیسک را پر می‌کند، جای درست، یک پلن با فضای NVMe بیشتر است؛ برای مثال سرور مجازی انگلستان با دیسک بزرگ‌تر جلوی همین بیرون‌ماندن ساعت دو نصف شب را می‌گیرد. اگر دیتابیس سنگین با بین‌لاگ حجیم داری و IO دیسک برایت حیاتی است، سرور اختصاصی کل دیسک را در اختیارت می‌گذارد و همسایه‌ای نداری که فضا و IO را با تو شریک باشد.

جلوگیری از تکرار

پاک کردن دیسک بدون بستن سوراخ، فقط تعویق است. سه کار که تکرار را واقعاً می‌بندد:

  1. چرخش لاگ. برای هر لاگی که در /var/log بزرگ می‌شود یک فایل در /etc/logrotate.d/ بگذار با rotate و maxsize مشخص. سقف journald را هم با SystemMaxUse ببند.
  2. پایش با آستانه. یک هشدار ساده روی ۸۰٪ اشغال دیسک بگذار. حتی یک کرون‌جاب که وقتی df از آستانه رد شد ایمیل بزند، تو را قبل از ساعت دو نصف شب بیدار می‌کند.
  3. پاک‌سازی خودکار. برای داکر یک کرون‌جاب docker system prune -af --filter "until=72h" و برای MySQL همان binlog_expire_logs_seconds.

اگر می‌خواهی بدانی از اول چقدر دیسک بگیری تا این اتفاق کمتر بیفتد، راهنمای ما درباره‌ی انتخاب رم، سی‌پی‌یو و دیسک VPS و تفاوت NVMe و SSD نقطه‌ی خوبی برای شروع است.

کجا اجرایش کنی

برای بار کاری‌ای که لاگ و دیتابیس مدام تولید می‌کند، دو چیز مهم است: فضای NVMe کافی که زود پر نشود، و دسترسی کنسول خارج از باند تا وقتی SSH خوابید بیرون نمانی. سرور مجازی انگلستان برای وقتی مناسب است که کاربرانت اروپایی‌اند و فضای بیشتر می‌خواهی؛ اگر بیشتر IO و دیسک اختصاصی لازم داری، سرور اختصاصی انتخاب بعدی است. تفاوت واقعی این است: روی پلن درست، پر شدن دیسک یک هشدار ۸۰٪ می‌شود، نه یک بیرون‌ماندن ساعت دو نصف شب.

منابع

  1. Fix "No Space Left on Device" When df Shows Free Space (unknown)
  2. Disk Full but df Shows Space: Deleted-File Handles and inode Exhaustion (unknown)
  3. How to Troubleshoot No Space Left on Device Errors on RHEL 9 (2026-03)
  4. MySQL 8.4 Reference Manual: PURGE BINARY LOGS Statement (unknown)
  5. journald.conf(5) — systemd — Debian Manpages (unknown)

پرسش‌های پرتکرار

دیسک پر است ولی SSH وصل نمی‌شود، چه کنم؟

از کنسول وب پنل مجازی‌سازت (VNC/KVM) وارد شو و چند صد مگابایت آزاد کن: sudo journalctl --vacuum-size=200M، sudo apt-get clean، و sudo truncate -s 0 روی بزرگ‌ترین لاگ. همین که کمی جا باز شود، سرویس‌ها بالا می‌آیند و می‌توانی درست تشخیص بدهی.

df می‌گوید دیسک پر است ولی du چیزی پیدا نمی‌کند، چرا؟

یک فایل حذف‌شده هنوز توسط پروسه‌ای باز نگه داشته شده. du فقط فایل‌های دارای نام را می‌شمارد، ولی df بلاک‌های فایل حذف‌شده‌ی بازمانده را هم حساب می‌کند. با sudo lsof +L1 پروسه را پیدا کن و همان سرویس را ری‌استارت کن تا فضا برگردد.

فرق df -h و df -i در چیست؟

df -h اشغال حجم بلاک‌ها را نشان می‌دهد و df -i اشغال inodeها را. هر دو خطای No space left on device می‌دهند، اما اگر df -h خالی باشد و df -i روی ۱۰۰٪، inodeها تمام شده‌اند؛ یعنی تعداد زیادی فایل ریز داری، نه چند فایل بزرگ، و باید دنبال پوشه‌ای بگردی که فایل‌های نجومی دارد.

چطور بین‌لاگ MySQL را بی‌خطر پاک کنم؟

هیچ‌وقت فایل‌های mysql-bin.* را با rm پاک نکن چون ایندکس داخلی خراب می‌شود. به‌جایش وصل شو و PURGE BINARY LOGS BEFORE DATE_SUB(NOW(), INTERVAL 2 DAY) بزن، و اگر ریپلیکا داری اول مطمئن شو موقعیت خواندنشان از این مرز گذشته. برای جلوگیری از تکرار binlog_expire_logs_seconds را ست کن.

پاک کردن کرنل‌های قدیمی که /boot را پر کرده‌اند بی‌خطر است؟

بله، روی اوبونتو و دبیان sudo apt-get autoremove --purge کرنل‌های قدیمی بلااستفاده را می‌برد و کرنل فعلی و یکی دو نسخه‌ی پشتیبان را نگه می‌دارد. با dpkg -l | grep linux-image می‌توانی قبلش ببینی چند کرنل نصب است.

سفارش سرور مجازی بازگشت به وبلاگ →