heroku 上必须使用 composer install --no-dev,否则会安装 dev 依赖导致构建变慢、slug 增大甚至内存超限被 kill;需设 composer_no_dev=1 或通过 .buildpacks/procfile 控制。

为什么 composer install --no-dev 在 Heroku 上不是可选,而是必须
Heroku 默认运行在 production 环境,但 Composer 不会自动跳过 require-dev 里的包——除非你显式告诉它。漏掉这个参数,会导致部署时安装一堆测试、Linter、PHPStan 等工具,拖慢构建、增大 slug(最终应用包),甚至因内存超限被 kill。
-
composer install在无--no-dev时,会读取composer.json全量安装,包括phpunit、friendsofphp/php-cs-fixer等 - Heroku 的 buildpack 默认不加该 flag,靠你自己控制:要么改
composer.json的config.platform,要么在Procfile或.buildpacks中干预 - 更稳的做法是:在项目根目录加
.env(Heroku 不加载它),但通过COMPOSER_NO_DEV=1环境变量强制生效 —— 这个变量会被官方 PHP buildpack 识别
如何让 Heroku 复用 Composer 缓存,避免每次重装全量依赖
默认情况下,Heroku 每次构建都是干净环境,vendor/ 不存在,Composer 只能从头下载和安装。但 buildpack 支持缓存 ~/.composer/cache,前提是你的依赖没频繁变锁文件或 PHP 版本。
- 确保
composer.lock提交到 Git —— 没它,缓存意义不大,因为 hash 不一致就跳过复用 - 不要在
composer.json里写"php": "^8.1"这种宽泛约束;Heroku 的 PHP buildpack 会按composer.lock里记录的 PHP minor 版本锁定运行时,版本漂移会导致缓存失效 - 如果用了私有包(如 GitLab 或自建 Satis),记得在
auth.json里配好 token,并把它加入.gitignore,再用heroku config:set COMPOSER_AUTH='{"http-basic":{"your.repo.com": {"username": "_", "password": "token"}}}'
什么时候该禁用 Heroku 的自动 Composer install,改用自定义构建步骤
当你需要执行 composer install 前后操作(如生成 autoload map、运行 php artisan package:discover、清理 dev-only 配置),或者依赖安装逻辑复杂(多 stage 构建、条件安装),官方 buildpack 的黑盒行为就会成为障碍。
- 删掉
.buildpacks里的https://github.com/heroku/heroku-buildpack-php,换成自定义脚本 - 在
bin/compile(需 chmod +x)里手动调用composer install --no-dev --optimize-autoloader --apcu-autoloader,并加php artisan config:clear && php artisan cache:clear等 - 注意:自定义构建会绕过 buildpack 的 PHP 版本检测和扩展自动加载,你得自己在
composer.json的config.platform.php和ext-xxx字段写清楚
COMPOSER_MEMORY_LIMIT 设太小会直接导致 Heroku 构建失败
Composer 7+ 默认限制内存为 1.5G,而 Heroku 的免费/ Hobby dyno 构建环境只有 ~1.2G 可用内存。一旦解析依赖图或解压大包(如 symfony/symfony 完整版),就会报 Killed 并中断构建,错误日志里看不到明显 PHP 错误,只有一行 ! Push rejected, failed to compile PHP app.
- 在本地试构建时,用
COMPOSER_MEMORY_LIMIT=-1 composer install --no-dev看是否稳定;若稳定,说明是内存问题 - 在 Heroku 上设
heroku config:set COMPOSER_MEMORY_LIMIT=-1—— 注意不是0,-1才表示不限制 - 更可持续的解法是精简
require:比如用symfony/framework-bundle替代symfony/symfony,避免加载整个组件树
缓存是否生效、内存是否够用、dev 包是否真的被排除——这三个点不逐个确认,光看部署日志“success”,不代表下次还能过。










