# تشخیص «ری‌استارت» سرور روی Coolify + رفع نویز لاگ + بررسی خطای ACME

## پروژه

`clinicpro` (فقط فایل‌های deploy — بدون تغییر در کد PHP/React)

## زمینه

کاربر در لاگ‌های production (Coolify روی Ubuntu) می‌بیند که workerها مدام exit/spawn می‌شوند و نگران است سرور down می‌شود. تحلیل لاگ ارائه‌شده:

```
2026-07-08 07:40:54,435 INFO exited: worker-scheduler (exit status 0; expected)
2026-07-08 07:40:54,457 INFO spawned: 'worker-scheduler' with pid 1464
2026-07-08 07:40:54,460 INFO exited: worker-async (exit status 0; expected)
2026-07-08 07:40:54,468 INFO spawned: 'worker-async' with pid 1465
2026-07-08 07:40:55,674 INFO success: worker-async entered RUNNING state ...
```

**این crash نیست.** این رفتار طراحی‌شده است:

- `docker/supervisord.conf` هر دو worker را با `--time-limit=3600` اجرا می‌کند → هر worker دقیقاً هر ۱ ساعت با exit code 0 خارج می‌شود (به همین دلیل supervisor می‌نویسد `expected`).
- supervisord با `autorestart=true` در کمتر از ۱ ثانیه دوباره spawn می‌کند.
- در تمام مدت لاگ، `GET /health` هر ۱۵ ثانیه `200` برگردانده — یعنی nginx + php-fpm (وب‌سرور اصلی) هرگز down نبوده.
- recycle ساعتیِ consumer الگوی استاندارد Symfony Messenger است (جلوگیری از نشت حافظه و connection کهنه به DB/Redis). مستندات Coolify هم healthcheck/restart policy را همین‌طور توصیه می‌کند.

**اما یک مشکل واقعی در لاگ هست** که می‌تواند در آینده باعث «down شدن» واقعی (خطای TLS) شود:

```
GET /.well-known/acme-challenge/hb1abFQX9d-... HTTP/1.1" 404
"Mozilla/5.0 (compatible; Let's Encrypt validation server; ...)"
```

سرور validation لتس‌انکریپت برای دامنه `clinic-pro.ir` چالش HTTP-01 فرستاده و به جای اینکه Traefik (پراکسی Coolify) خودش جواب بدهد، درخواست به کانتینر app رسیده و 404 گرفته. اگر این وضع ادامه پیدا کند تمدید گواهی شکست می‌خورد و بعد از انقضای cert فعلی، سایت با خطای TLS از دسترس خارج می‌شود — کاربر آن را «سرور down شد» می‌بیند.

## فایل‌های مرتبط

| فایل | نقش |
|------|-----|
| `docker/supervisord.conf` | supervisor داخل کانتینر: nginx + php-fpm + دو worker با `--time-limit=3600 -v` |
| `docker-compose.yml` | حالت Compose در Coolify: app + دو worker به‌صورت سرویس جدا، همان `--time-limit=3600 -v` |
| `supervisor.conf` | نسخه Liara — همان الگو |
| `docker/healthcheck.sh` | healthcheck کانتینر: `GET /health` → exit 0/1 |
| `Dockerfile` | `HEALTHCHECK --interval=15s --retries=5 --start-period=60s` |
| `docs/` | مقصد runbook جدید |

## وضعیت فعلی

`docker/supervisord.conf`:

```ini
[program:worker-async]
command=php bin/console messenger:consume async --time-limit=3600 --memory-limit=128M -v
autorestart=true

[program:worker-scheduler]
command=php bin/console messenger:consume scheduler_default --time-limit=3600 -v
autorestart=true
```

فلگ `-v` باعث می‌شود هر بار restart شدن worker، بنر چندخطی «[OK] Consuming messages…» و «The worker will automatically exit…» در stdout چاپ شود — همین بنرها لاگ را شلوغ و «ری‌استارت» را ترسناک‌تر از واقعیت نشان می‌دهند.

## وظایف

### ۱. کاهش نویز لاگ workerها (بدون تغییر رفتار)

فلگ `-v` را از هر چهار محل حذف کن (دو program در `docker/supervisord.conf`، دو service در `docker-compose.yml`، دو program در `supervisor.conf`). `--time-limit=3600` و `--memory-limit=128M` را **دست نزن** — recycle ساعتی عمداً وجود دارد و باید بماند.

```ini
command=php bin/console messenger:consume async --time-limit=3600 --memory-limit=128M
```

خطاهای واقعی worker همچنان از طریق monolog (stderr) دیده می‌شوند؛ `-v` فقط verbosity کنسول بود.

### ۲. Runbook تشخیص «restart واقعی» از «recycle عادی»

فایل `docs/ops-restarts.md` بساز (فارسی) با این محتوا:

- توضیح اینکه `exited (exit status 0; expected)` + spawn فوری = recycle عادی و بی‌ضرر است؛ نشانه‌های مشکل واقعی: `exit status` غیرصفر، `entered FATAL state`، `too many start retries`، یا گپ در لاگ‌های `/health`.
- دستورهای تشخیص روی سرور:

```bash
# آیا خودِ کانتینر ری‌استارت شده؟ (uptime کانتینر vs uptime پروسه)
docker ps --format 'table {{.Names}}\t{{.Status}}'   # Status باید Up X hours (healthy) باشد
docker inspect --format '{{.RestartCount}} {{.State.StartedAt}}' <container>
docker events --since 24h --filter event=restart --filter event=die
```

- اگر `RestartCount` صفر است و `StartedAt` قدیمی → هیچ ری‌استارت واقعی‌ای در کار نیست؛ فقط recycle داخلی worker است.
- بخش Coolify: فعال‌کردن Notifications (Telegram/Email) در Coolify برای event های container stop/unhealthy، تا down شدن واقعی بلافاصله اطلاع داده شود. ارجاع به مستندات: `https://coolify.io/docs/knowledge-base/notifications` و `https://coolify.io/docs/knowledge-base/health-checks`.

### ۳. بررسی و مستندسازی خطای ACME (مهم‌ترین ریسک down شدن واقعی)

در همان `docs/ops-restarts.md` بخش «تمدید گواهی TLS» اضافه کن:

- علامت مشکل: `GET /.well-known/acme-challenge/... → 404` در access log کانتینر app. چالش HTTP-01 باید توسط Traefik خود Coolify جواب داده شود و اصلاً نباید به app برسد؛ رسیدنش یعنی صدور/تمدید cert برای آن دامنه دارد شکست می‌خورد.
- چک‌لیست رفع روی سرور Coolify:
  1. در Coolify → resource مربوط به app → تب Domains: مطمئن شو دامنه `clinic-pro.ir` (و `www` اگر لازم است) دقیقاً با schema `https://` روی سرویس `app` ثبت شده باشد. دامنه‌ای که در Coolify ثبت نشده ولی DNS آن به سرور اشاره می‌کند، همین الگوی 404 را می‌سازد.
  2. وضعیت فعلی cert را چک کن: `echo | openssl s_client -connect clinic-pro.ir:443 -servername clinic-pro.ir 2>/dev/null | openssl x509 -noout -dates`
  3. لاگ Traefik/proxy در Coolify (Servers → Proxy → Logs) را برای خطای acme بررسی کن.
  - ارجاع: `https://coolify.io/docs/knowledge-base/proxy/traefik/overview` و بخش Domains در مستندات Coolify.

### ۴. سازگاری healthcheck

فقط بررسی (بدون تغییر مگر مغایرت پیدا شد): `Dockerfile` مقادیر `interval=15s, retries=5, start-period=60s` دارد و `docker-compose.yml` سرویس app همان مقادیر را — مطمئن شو یکسان بمانند. healthcheck های worker در compose (`ps | grep messenger:consume`) در لحظه exit ساعتیِ worker ممکن است یک بار fail شوند؛ چون `retries: 3` و `interval: 30s` است، سه شکست پیاپی لازم است و restart کانتینر (که Coolify آن را event می‌کند) رخ نمی‌دهد مگر worker واقعاً بالا نیاید — این را در runbook هم ذکر کن.

## نکات مهم

- **هیچ تغییری در رفتار runtime نده** جز حذف `-v`. `--time-limit` و `autorestart` عمدی و صحیح‌اند.
- این پرامپت فایل PHP/React را لمس نمی‌کند → migration و به‌روزرسانی `docs/api/` لازم ندارد.
- `docs/ops-restarts.md` فارسی نوشته شود (زبان محصول)، ولی دستورها و خروجی‌های shell انگلیسی بمانند.
- سه فایل config (`docker/supervisord.conf`، `docker-compose.yml`، `supervisor.conf`) باید بعد از تغییر هم‌چنان آینه هم بمانند — کامنت‌های بالای هر فایل همین را می‌گویند.
- بعد از merge، اعمال روی سرور نیاز به redeploy در Coolify دارد؛ در runbook یادآوری کن.
