
به اشتراک بگذارید

به اشتراک بگذارید
پیغام could not be opened in append mode کنار عبارت Permission denied برای فایل storage/logs/laravel.log از اولین پیغامهایی است که بعد از استقرار لاراول روی سرور در لاگ میبینید. روی لپتاپ، php artisan serve با کاربر خودتان اجرا میشود و هشت مورد زیر را پنهان میکند.
.env ویرایششده با کلید؛ اگر نباشد: SQLite و حالت debug.schedule:run؛ اگر نباشد: زمانبندی اجرا نمیشود.💡 PHP 8.3 و یک پیششرط شبکه
برای Laravel 13 دستکم PHP 8.3 لازم است.
مورد ۲ تنها بازرسیای است که به بیرون از سرور گره خورده: Packagist و رجیستری npm. روی سرور داخل ایران این مورد را با راهنمای تحریم npm کنار هم تیک بزنید.
اگر همین حالا متن خطایی جلوی چشمتان است، بازرسی را از آخر شروع کنید: متن را در فهرست معکوس خطاها انتهای بخش کش بیابید تا شماره مورد معلوم شود.
چهار ایستگاه بازرسی
◀️ سرور پیش از لاراول؛ نسخه PHP، افزونهها و Composer
◀️ پوشه public، مجوز storage و بلوک Nginx را درست بچینید
◀️ صف و زمانبندی؛ اگر Supervisor و کرون نباشند چه چیزی از کار میافتد
◀️ کش پیکربندی در Production کمک است یا دردسر؟

طبق مستندات Laravel 13، استقرار لاراول روی سرور دستکم به PHP 8.3 نیاز دارد، بههمراه افزونههای Ctype، cURL، DOM، Fileinfo، Filter، Hash، Mbstring، OpenSSL، PCRE، PDO، Session، Tokenizer و XML. در Laravel 12 حداقل 8.2 است. Nginx، PHP-FPM و دیتابیس را آماده فرض کردهایم؛ انتقال فایل و دیتابیس از هاست اشتراکی در راهنمای انتقال سایت به سرور مجازی آمده است.
دستور check-platform-reqs PHP و افزونههای واقعی سرور را با نیاز پکیجها مقایسه میکند و با --lock پیش از نصب هم جواب میدهد. هر ردیف missing یک افزونه نصبنشده است.
composer check-platform-reqs --lock --no-dev
در مستندات Composer، --no-dev پکیجهای require-dev را کنار میگذارد و --optimize-autoloader، که برای Production توصیه شده، PSR-4 را به classmap تبدیل میکند. اگر پروژه Vite دارد، بدون npm run build صفحه با Vite manifest not found میایستد.
composer install --no-dev --optimize-autoloader
npm ci && npm run build
نمونه .env.example در Laravel 13 با APP_ENV=local، APP_DEBUG=true و DB_CONNECTION=sqlite میآید. بدون ویرایش، migrate --force بیصدا فایل SQLite میسازد و debug روشن به گفته مستندات ممکن است تنظیمات حساس را نشان دهد. APP_KEY خالی هم پیغام No application encryption key has been specified. میدهد.
cp .env.example .env # فقط در استقرار اول
nano .env
# APP_ENV=production APP_DEBUG=false APP_URL=https://example.com
# DB_CONNECTION=mysql DB_HOST DB_DATABASE DB_USERNAME DB_PASSWORD
php artisan key:generate # فقط وقتی APP_KEY خالی است
کلید برنامهای را که کاربر دارد عوض نکنید؛ نشستها بسته میشوند و داده رمزشده قبلی باز نمیشود. migrate در مورد ۸ اجرا میشود.

وبسرور Nginx فقط باید پوشه public را ببیند و PHP-FPM باید در storage و bootstrap/cache بنویسد؛ این دو شرط در استقرار لاراول روی سرور جلوی بیشتر خطاهای 403 و 500 را میگیرند. مستندات لاراول میگوید index.php را به ریشه پروژه نبرید، وگرنه فایلهای حساس پیکربندی باز میشوند.
مدیر صحنه تئاتر پیش از بالا رفتن پرده، نور و جای سوفلور را تیک میزند، چون هر قلم جا افتاده یک لحظه از نمایش را خراب میکند. دو قلم این بخش هم همینطورند.
بلوک کامل، با هدرهای امنیتی و بافر FastCGI، در مستندات لاراول است؛ کد زیر بخش پرخطای آن است. بدون index index.php صفحه اصلی 403 میدهد و fastcgi_pass باید همان سوکت خط listen استخر PHP-FPM باشد. دو بلوک آخر فایلهای PHP دیگر و فایلهای نقطهدار مثل .env را میبندند؛ بعد از هر تغییر sudo nginx -t بزنید.
# بخشی از بلوک server
root /srv/example.com/public;
index index.php;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ ^/index\.php(/|$) {
fastcgi_pass unix:/var/run/php/php8.3-fpm.sock;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
}
location ~ \.php$ {
return 404;
}
location ~ /\.(?!well-known).* {
deny all;
}
با PHP-FPM، نویسنده storage کاربر خط user فایل pool است که مستندات PHP-FPM آن را «کاربر یونیکس پروسههای FPM» مینامد. دستور اول فقط ردیفهای pool را نشان میدهد؛ در مثال www-data فرض شده.
بقیه کد مال یک کاربر استقرار (مثلاً deploy) عضو گروه www-data باشد. git و composer را با همین کاربر و artisan را با sudo -u www-data اجرا کنید تا فایلی با مالک root ساخته نشود.
ps -eo user,args | grep "[p]ool"
sudo usermod -aG www-data deploy
mkdir -p storage/framework/{cache,sessions,views}
sudo chown -R www-data:www-data storage bootstrap/cache
sudo chmod -R ug+rwX storage bootstrap/cache
⚠️ سراغ chmod 777 نروید
عدد 777 خطا را برطرف میکند، ولی به هر پروسه روی سرور اجازه نوشتن میدهد. اسکریپت آلودهای روی همان سرور میتواند در ویوهای کامپایلشده شما کد بنویسد.
| مورد | اگر جا بیفتد | نشانه در لاگ |
|---|---|---|
| ۴: خط index | صفحه اصلی 403 | پیغام directory index of … is forbidden |
| ۴: سوکت fastcgi_pass | همه صفحهها 502 | پیغام connect() to unix:… failed |
| ۵: مالکیت storage | خطای 500 | پیغام could not be opened in append mode |
| ۵: نبودن storage/framework | ویوها کامپایل نمیشوند؛ پوشه را بسازید | پیغام Please provide a valid cache path. |
هر Job تا پردازش با queue:work در صف میماند. مستندات صف لاراول اتصال پیشفرض صف را database گذاشته است؛ اگر بعد از استقرار لاراول روی سرور ایمیلها را با صف میفرستید، بدون پردازشگر فقط جدول jobs پر میشود.
پروسه queue:work با تمام شدن --max-time یا با queue:restart متوقف میشود و Supervisor دوباره اجرایش میکند. نصب روی Ubuntu با sudo apt-get install supervisor است. نمونه زیر از مستندات لاراول است؛ آن را با پسوند .conf در /etc/supervisor/conf.d ذخیره کنید.
; /etc/supervisor/conf.d/laravel-worker.conf
[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /srv/example.com/artisan queue:work --sleep=3 --tries=3 --max-time=3600
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=www-data
numprocs=2
redirect_stderr=true
stdout_logfile=/srv/example.com/storage/logs/worker.log
stopwaitsecs=3600
هر پروسهای که numprocs میسازد یک PHP کامل در حافظه است. stopwaitsecs باید از طولانیترین Job بیشتر باشد، وگرنه Supervisor ممکن است کار را نیمهکاره متوقف کند. سپس:
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start "laravel-worker:*"
دستور schedule:run، به گفته مستندات زمانبندی لاراول، هر دقیقه کارهای موعددار را اجرا میکند. خط زیر را با sudo crontab -u www-data -e اضافه کنید و با php artisan schedule:list وارسی کنید.
* * * * * cd /srv/example.com && php artisan schedule:run >> /dev/null 2>&1
| مورد | اگر جا بیفتد | نشانه در لاگ |
|---|---|---|
| ۶: Supervisor | ایمیل صفشده نمیرسد | فایل worker.log خالی یا ساختهنشده |
۶: پسوند .conf | پیغام no such group | پردازشگر هرگز اجرا نشده |
| ۷: کرون | کار زمانبندیشده اجرا نمیشود | لاگی نیست، چون چیزی اجرا نشده |

حکم کوتاه است: کش کمک است، اگر بعد از هر تغییر .env دوباره ساخته شود. php artisan optimize کش پیکربندی، رویدادها، مسیرها و ویوها را میسازد و مستندات آن را بخشی از هر بار استقرار لاراول روی سرور میداند.
🔸 چرا تغییر فایل محیط (.env) بعد از config:cache دیده نمیشود
بعد از config:cache، لاراول .env را بار نمیکند و env() برای متغیرهایش null برمیگرداند. env() را فقط در فایلهای config بنویسید و در بقیه کد config() را به کار ببرید.
# در پوشه پروژه، با کاربر deploy
sudo -u www-data php artisan down
git pull
composer install --no-dev --optimize-autoloader
npm ci && npm run build
# پیش از migrate از دیتابیس بکاپ بگیرید
sudo -u www-data php artisan migrate --force
sudo -u www-data php artisan optimize
sudo -u www-data php artisan queue:restart
sudo -u www-data php artisan up
⚠️ پردازشگر صف کد قدیمی را نگه میدارد
پروسههای queue:work تا ریستارت نشوند کد تازه را نمیبینند. queue:restart آنها را بعد از Job فعلی خارج میکند و Supervisor با کد جدید اجرایشان میکند؛ این سیگنال در کش ذخیره میشود.
🔶 تمرین استقرار روی سیستمعامل تازه
چکلیست را پیش از بردن دامنه روی سرور خالی تمرین کنید؛ نصب مجدد سیستمعامل از پنل سرور مجازی وبداده هر دور را از صفر شروع میکند.
ext- ← مورد ۱Vite manifest not found ← مورد ۲No application encryption key has been specified. یا فایل SQLite ناخواسته ← مورد ۳File not found. ← مورد ۴could not be opened in append mode یا Please provide a valid cache path. (پوشه نیست) ← مورد ۵env() یا Job با کد کهنه ← مورد ۸سایت بالاست، اما ایمیلی نرسیده یا تنظیمی اثر نکرده است؛ اینها گرفتاریهای هفته اول بعد از راهاندازیاند.
اگر کش پیکربندی دارید، php artisan optimize را دوباره اجرا کنید و صف را ریستارت کنید؛ تا آن موقع مقدار کش قبلی خوانده میشود.
فایلی که آن دستور ساخته مالک root دارد و PHP-FPM در آن نمینویسد. مالکیت storage را برگردانید و artisan را با sudo -u www-data اجرا کنید.
مسیر سلامت پیشفرض لاراول /up است؛ اگر برنامه بدون خطا بوت شود 200 و در غیر این صورت 500 برمیگرداند.
اگر فایلهای آپلودی دیسک public باید از وب باز شوند، بله؛ این دستور public/storage را به storage/app/public پیوند میدهد.
هر Job ناموفق در جدول failed_jobs ثبت میشود. php artisan queue:failed شناسهها را نشان میدهد و queue:retry با شناسه، Job را به صف برمیگرداند.
عدد ثابتی در کار نیست، هرچند همه دنبالشاند. با یک یا دو پروسه شروع کنید و با دیدن مصرف رم، numprocs را بالا ببرید.
اول ببینید خطا 403 است یا timeout؛ هر دو حالت در راهنمای کادر پایین آمدهاند.
جمعبندی؛ استقرار لاراول روی سرور با هشت بازرسی خطاهای روز اول از چیزهایی میآیند که روی لپتاپ نبودند. نسخه PHP را با check-platform-reqs بسنجید، وابستگیها را بدون پکیج توسعه نصب کنید و فرانت را build کنید. فایل .env را پیش از migrate با APP_ENV=production، APP_DEBUG=false و اطلاعات دیتابیس ویرایش کنید. ریشه Nginx را روی public بگذارید، کد را به کاربر استقرار و storage را به کاربر PHP-FPM بسپارید. صف را به Supervisor و زمانبندی را به کرون بدهید. در پایان هر استقرار، کش را با optimize بسازید و صف را ریستارت کنید. خطای تازه را با فهرست معکوس بسنجید.
🚀 نصب پکیج روی سرور ایران متوقف شده؟
مورد ۲ به Packagist و npm وابسته است؛ فرق 403 با timeout روی سرور داخل ایران و تنظیم میرور در یک راهنما آمدهاند.
اولین خطایی که بعد از بردن پروژه لاراول به سرور دیدید چه بود؟ متن دقیقش را در دیدگاهها بگذارید تا فهرست معکوس کاملتر شود.
منابع