线上部署必须分步执行:先拉取新代码到带时间戳目录,再执行迁移和缓存重建,最后原子切换current软链;同时需重启supervisord托管的队列/定时任务进程,并确保nginx root指向public子目录及storage软链正确。

更新代码时服务中断,是因为没用 git pull + php artisan migrate 的顺序控制
直接在生产环境 git pull 后立刻跑 php artisan migrate,极大概率触发数据库结构变更与旧代码逻辑不匹配——比如新迁移加了非空字段,但旧代码还在往该表插入数据,请求直接 500。真实线上不是本地,没回滚按钮。
必须把「代码就位」和「结构就位」拆开:先切到新代码(但不激活),再安全执行迁移,最后原子切换。Laravel 自身不提供零停机能力,得靠部署层配合。
- 每次发布前,在新目录拉取代码:
git clone或git archive到带时间戳的目录(如/var/www/myapp/releases/20241105-1422) - 在该目录内执行
composer install --no-dev --optimize-autoloader和php artisan migrate --force - 确保
.env是软链到共享目录(/var/www/myapp/shared/.env),不随 release 变动 - 最后用原子操作切换
current软链:ln -sf /var/www/myapp/releases/20241105-1422 /var/www/myapp/current
队列、定时任务、WebSocket 进程没重启,导致新代码不生效
Web 请求走 Nginx → PHP-FPM,切了 current 就立即生效;但 php artisan queue:work、php artisan schedule:run 或 php artisan websockets:serve 这些是长进程,它们加载的是启动时的代码路径,不会自动感知 current 变更。
常见现象:页面显示新功能,但队列任务仍执行旧逻辑,或 Cron 报错找不到新模型里的方法。
- 所有后台进程必须用进程管理器(如
supervisord)托管,且其配置中command指向/var/www/myapp/current/artisan - 每次部署后,强制重启相关进程:
sudo supervisorctl restart myapp-queue、sudo supervisorctl restart myapp-schedule - 别用
nohup php artisan queue:work &这种裸启方式——它永远卡在第一次启动的 release 目录里
php artisan config:cache 和 view:cache 在部署后必须重生成
缓存文件(bootstrap/cache/config.php、storage/framework/views/xxx.php)是硬编码路径+内容的 PHP 文件,一旦 release 目录变了,缓存里还记着旧路径下的配置或视图,轻则配置读错,重则 include(): Failed opening 致命错误。
尤其当 .env 有变动(如 APP_ENV=production → APP_ENV=staging),不重刷 config cache,环境变量根本不会生效。
- 部署脚本末尾必须加:
php /var/www/myapp/current/artisan config:clear && php /var/www/myapp/current/artisan config:cache - 如果用了 Blade 编译视图(非开发模式),顺手清 view cache:
php /var/www/myapp/current/artisan view:clear && php /var/www/myapp/current/artisan view:cache - 注意:不要在
config:cache前运行config:clear——后者会删掉config.php,但前者依赖它存在才能合并,直接报错
Nginx 静态资源 404,因为没更新 root 或漏了 public 子目录
很多人把 Nginx 的 root 指向 /var/www/myapp/current,但 Laravel 的入口是 public/,结果所有 CSS/JS/图片全 404,连登录页都打不开。
另一个坑是:部署脚本复制了整个 release 目录,但忘了把 public/storage 软链重新指向 ../shared/storage/app/public,导致用户上传的图片丢失。
- Nginx 配置里
root必须设为/var/www/myapp/current/public,不是current根目录 - 每次部署后检查软链:
ls -l /var/www/myapp/current/public/storage,应指向../../shared/storage/app/public - 如果用了
php artisan storage:link,它只在当前目录建链,不能替代手动维护共享存储链











