14 KiB
آمادهسازی پروژه ClinicPro برای دیپلوی روی Coolify (Symfony + Nixpacks)
زمینه
پروژه قرار است روی Coolify با Build Pack = Nixpacks دیپلوی شود (طبق https://coolify.io/docs/applications/symfony).
در حال حاضر پروژه فقط برای محیط لوکال ddev پیکربندی شده است (compose.yaml، .ddev/).
برای Coolify نیاز به این موارد داریم که هیچکدام الان وجود ندارند:
- کنترل کامل بر فرایند build — چون این پروژه علاوه بر PHP یک فرانتاند React 19 + Webpack Encore دارد که باید با
yarn buildکامپایل شود (Nixpacks پیشفرض Symfony فقط PHP را build میکند و داراییهایpublic/build/ساخته نمیشوند). - تولید کلیدهای JWT (lexik) هنگام دیپلوی — این کلیدها در
.gitignoreهستند و در ریپو نیستند. - اجرای migrations بعد از هر دیپلوی.
- اجرای workerهای Messenger:
messenger:consume async→ ارسال SMSmessenger:consume scheduler_default→ انقضای نوبتهای پرداختنشده (هر ۱ دقیقه)
- تنظیم Trusted Proxies (چون Coolify پشت Traefik/reverse-proxy است؛ بدون آن
https، IP کلاینت و کوکیها درست کار نمیکنند). - یک فایل
.env.coolify.exampleکه همهٔ متغیرهای محیطی لازم را برای وارد کردن در داشبورد Coolify فهرست کند.
نکتهٔ مهم معماری: دیتابیس پروژه MariaDB/MySQL است (نه PostgreSQL که داک Coolify مثال میزند). Redis برای Messenger و OTP استفاده میشود.
هدف
پروژه را طوری «coolify-ready» کن که با Build Pack = Nixpacks و چند متغیر محیطی، بدون تغییر در منطق برنامه، روی Coolify بالا بیاید — شامل build فرانتاند، تولید کلید JWT، migration، و راهاندازی workerها.
فایلهای مرتبط
| فایل | نقش | وضعیت |
|---|---|---|
nixpacks.toml |
کنترل فازهای build/install/start برای Nixpacks | باید ساخته شود |
config/packages/framework.yaml |
افزودن trusted_proxies و trusted_headers |
باید ویرایش شود |
.env.coolify.example |
فهرست کامل env برای داشبورد Coolify | باید ساخته شود |
docs/deploy/coolify.md |
راهنمای گامبهگام دیپلوی | باید ساخته شود |
.env.example |
مرجع موجود متغیرها (فقط خواندن) | بدون تغییر |
package.json |
اسکریپت build = encore production |
بدون تغییر |
config/packages/messenger.yaml |
تعریف transport های async و scheduler_default |
بدون تغییر (مرجع worker) |
وضعیت فعلی
package.jsonاسکریپت build دارد:"build": "encore production --progress".- خروجی build در
public/build/میرود (در.gitignoreاست → باید حین دیپلوی ساخته شود). - کلیدهای JWT در
config/jwt/*.pem(در.gitignore). framework.yamlفعلی هیچtrusted_proxiesندارد:
framework:
secret: '%env(APP_SECRET)%'
session: true
- متغیرهای کلیدی از
.env.example:APP_ENV,APP_SECRET,DATABASE_URL(mysql),JWT_*,CORS_ALLOW_ORIGIN,MESSENGER_TRANSPORT_DSN(redis),REDIS_URL,API_IR_*,APP_BASE_URL,ALLOWED_FRONTEND_HOSTS,DEFAULT_URI,MAX_FILE_SIZE_BYTES,UPLOAD_DIR.
وظایف
۱. ساخت nixpacks.toml در ریشهٔ پروژه
هدف: علاوه بر نصب وابستگیهای PHP، فرانتاند را هم build کن و کلید JWT بساز. Nixpacks باید هم PHP و هم Node/Yarn را در محیط build داشته باشد.
# nixpacks.toml — کنترل دیپلوی Coolify
[phases.setup]
nixPkgs = ["php82", "php82Packages.composer", "nodejs_20", "yarn", "openssl"]
[phases.install]
cmds = [
"composer install --no-dev --optimize-autoloader --no-interaction --prefer-dist",
"yarn install --frozen-lockfile"
]
[phases.build]
# build داراییهای فرانتاند (React/Encore) → public/build
# و تولید کلید JWT اگر وجود نداشته باشد (idempotent)
cmds = [
"yarn build",
"php bin/console lexik:jwt:generate-keypair --skip-if-exists --no-interaction"
]
[start]
# Nixpacks خودش php-fpm/nginx را با NIXPACKS_PHP_ROOT_DIR اجرا میکند؛
# این بخش را فقط در صورت نیاز override کن. در حالت عادی خالی بماند.
نکات:
- نسخهٔ
php82باید با"php": ">=8.2"درcomposer.jsonهمخوان باشد. اگر Nixpacks نسخهٔ PHP را خودکار از composer تشخیص میدهد، میتوانphpرا ازnixPkgsحذف کرد — ولی صریح بودن امنتر است. lexik:jwt:generate-keypair --skip-if-existsباعث میشود اگر کلید از قبل (مثلاً از volume) وجود داشته باشد، دوباره ساخته نشود. هشدار را در docs ذکر کن: اگر کلید JWT بین دیپلویها persist نشود، همهٔ توکنهای صادرشده باطل میشوند → بهتر استJWT_SECRET_KEY/JWT_PUBLIC_KEYبهصورت محتوای base64 در env داده شوند یا روی volume مانت شوند. هر دو گزینه را در docs توضیح بده.- اگر
JWT_PASSPHRASEدر env تنظیم نشده باشد، تولید کلید fail میشود — این را در docs قید کن.
۲. افزودن Trusted Proxies به config/packages/framework.yaml
Coolify پشت Traefik است. بدون این تنظیم، Symfony پروتکل https و IP واقعی را تشخیص نمیدهد.
framework:
secret: '%env(APP_SECRET)%'
session: true
# Coolify/Traefik reverse proxy
trusted_proxies: '%env(TRUSTED_PROXIES)%'
trusted_headers: ['x-forwarded-for', 'x-forwarded-host', 'x-forwarded-proto', 'x-forwarded-port']
و در .env.coolify.example مقدار پیشفرض امن بده (طبق داک Coolify که از REMOTE_ADDR یا رنج شبکهٔ داکر استفاده میشود):
# IP/رنج reverse proxy؛ برای Coolify معمولاً رنج شبکهٔ داخلی داکر یا 'REMOTE_ADDR'
TRUSTED_PROXIES=10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,127.0.0.1
نکته: اگر TRUSTED_PROXIES خالی بماند، Symfony خطا نمیدهد ولی پشت پراکسی درست کار نمیکند؛ مقدار پیشفرض را در env بگذار تا اگر کاربر فراموش کرد، رفتار منطقی باشد. اگر ترجیح میدهی fallback داشته باشی از '%env(default::TRUSTED_PROXIES)%' با مقدار پیشفرض در .env استفاده کن.
۳. ساخت .env.coolify.example
فهرست کامل و دستهبندیشدهٔ تمام متغیرهایی که باید در داشبورد Coolify (Environment Variables) وارد شوند. از روی .env.example بساز ولی این موارد Coolify-specific را اضافه کن:
# ───── Coolify / Nixpacks (الزامی طبق داک Coolify) ─────
APP_ENV=prod
APP_DEBUG=0
NIXPACKS_PHP_FALLBACK_PATH=/index.php
NIXPACKS_PHP_ROOT_DIR=/app/public
# Port Exposure را در UI روی 80 بگذار
# ───── Symfony Core ─────
APP_SECRET= # php -r "echo bin2hex(random_bytes(32));"
DEFAULT_URI=https://your-domain.com
APP_BASE_URL=https://your-domain.com
ALLOWED_FRONTEND_HOSTS=your-domain.com
# ───── Reverse Proxy (Coolify/Traefik) ─────
TRUSTED_PROXIES=10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,127.0.0.1
# ───── Database (MariaDB/MySQL service در Coolify) ─────
DATABASE_URL="mysql://USER:PASS@HOST:3306/clinic_pro?serverVersion=mariadb-11.8.0&charset=utf8mb4"
# ───── JWT (lexik) ─────
JWT_SECRET_KEY=%kernel.project_dir%/config/jwt/private.pem
JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public.pem
JWT_PASSPHRASE= # الزامی برای تولید کلید در فاز build
# ───── CORS ─────
CORS_ALLOW_ORIGIN='^https://your-domain\.com$'
# ───── Messenger / Redis (Redis service در Coolify) ─────
MESSENGER_TRANSPORT_DSN=redis://HOST:6379/messages
REDIS_URL=redis://HOST:6379
# ───── Auth / OTP ─────
REFRESH_TOKEN_TTL=2592000
OTP_TTL=1200
# ───── api.ir (استعلام هویت) ─────
API_IR_BASE_URL=https://s.api.ir
API_IR_TOKEN=
# ───── File Upload ─────
MAX_FILE_SIZE_BYTES=5242880
UPLOAD_DIR=var/uploads
نکات مهم برای این فایل:
serverVersionدرDATABASE_URLرا با MariaDB همخوان کن (mariadb-11.8.0)، نه8.0، چون پروژه روی MariaDB 11.8 است.- توضیح بده که
HOSTدر DSNها = نام internal service که Coolify برای DB/Redis میدهد. - کلیدهای SMS و درگاه پرداخت از DB («تنظیمات سایت») خوانده میشوند، نه env — این را با کامنت ذکر کن.
۴. ساخت راهنمای docs/deploy/coolify.md
یک سند گامبهگام فارسی شامل:
- تنظیمات Coolify UI:
- Build Pack =
nixpacks - Port Exposure =
80 - Health Check Path =
/api/docیا یک endpoint سبک (بررسی کن چه endpoint عمومیای برای healthcheck مناسب است؛ ازconfig/packages/security.yamlلیست public_endpoints را ببین).
- Build Pack =
- Environment Variables: ارجاع به
.env.coolify.example+ متغیرهای الزامی Nixpacks. - Post-Deployment Command (در بخش مربوطهٔ Coolify):
php bin/console doctrine:migrations:migrate --all-or-nothing --no-interaction - Persistent Storage (Volumes) — این بخش حیاتی است، حتماً توضیح بده:
config/jwt/→ تا کلیدهای JWT بین دیپلویها باقی بمانند (در غیر این صورت همهٔ لاگینها باطل میشوند).public/uploads/وvar/uploads/→ فایلهای آپلودی کاربران.var/log/(اختیاری).
- Workerها (سرویسهای جداگانه در Coolify): توضیح بده که باید دو سرویس/کانتینر اضافه با همان image ولی start command متفاوت ساخته شوند، یا از Supervisor در همان کانتینر استفاده شود:
php bin/console messenger:consume async --time-limit=3600 --memory-limit=128Mphp bin/console messenger:consume scheduler_default --time-limit=3600گزینهٔ توصیهشده را مشخص کن (در Coolify معمولاً سرویس جداگانه تمیزتر است؛ یا یکsupervisord.confدر همان کانتینر).
- اولین راهاندازی: ساخت admin اولیه:
php bin/console app:create-admin
۵. (اختیاری ولی توصیهشده) فایل supervisord.conf برای workerها
اگر تصمیم گرفتی workerها در همان کانتینر اجرا شوند، یک supervisord.conf بساز که هر دو consumer را مدیریت کند و در nixpacks.toml به phase setup اضافهاش کن. در غیر این صورت، در docs گزینهٔ «سرویس جداگانه در Coolify» را بهعنوان روش پیشفرض توضیح بده و این فایل را نساز.
تصمیم پیشنهادی: سرویس جداگانه در Coolify (سادهتر، بدون نیاز به supervisor و بدون پیچیده کردن کانتینر اصلی). فقط در docs مستند کن.
نکات مهم (محدودیتها و edge caseها)
- MariaDB نه PostgreSQL: داک رسمی Coolify مثال PostgreSQL میزند؛ این پروژه MySQL/MariaDB است. همهجا DSN را
mysql://...باserverVersion=mariadb-11.8.0نگه دار. - build فرانتاند الزامی است: بدون
yarn build، صفحات admin (و داراییهای React) لود نمیشوند چونpublic/build/خالی میماند. این تفاوت اصلی با setup پیشفرض Symfony در داک Coolify است. - JWT keypair persistence: اگر volume برای
config/jwt/تعریف نشود، هر دیپلوی کلید جدید میسازد و کاربران logout میشوند. این مهمترین نکتهٔ عملیاتی است — حتماً در docs برجسته شود. JWT_PASSPHRASEباید قبل از build در env باشد وگرنهlexik:jwt:generate-keypairدر فاز build شکست میخورد.- Scheduler: transport
scheduler_defaultنوبتهای پرداختنشده را هر دقیقه منقضی میکند؛ اگر worker آن اجرا نشود، نوبتهای رزرو ولی پرداختنشده آزاد نمیشوند. این رفتار را در docs ذکر کن. - هیچ منطق برنامهای را تغییر نده — فقط فایلهای infra/config اضافه یا ویرایش شوند.
- بدون تغییر در ddev: فایلهای
compose.yamlو.ddev/دستنخورده بمانند تا محیط لوکال نشکند. - بعد از تغییر
framework.yaml،ddev exec php bin/console cache:clearبزن تا مطمئن شوی config معتبر است. - این تغییرات API را عوض نمیکنند، پس نیازی به بهروزرسانی
docs/api/*نیست؛ ولی فایل جدیدdocs/deploy/coolify.mdاضافه میشود.