Vapor部署最省心但需配好CLI和IAM权限,最小策略为AdministratorAccess-AWSElasticBeanstalk与AWSServiceRoleForElasticBeanstalk;EC2部署需处理软链接权限或改用绝对路径;S3 doesObjectExist失败多因Key未URL编码;严禁在.env中硬写AWS密钥,应使用IAM实例角色。

用 Vapor 部署最省心,但得先配好 CLI 和 IAM 权限
如果你的 Laravel 应用不依赖本地扩展(如 Imagick、SOAP)、不需要长期运行的守护进程(比如自定义 socket 服务),Vapor 是目前 AWS 上最顺滑的部署路径。它自动处理 Lambda、API Gateway、S3、CloudFront,连队列和缓存都托管了。
常见错误现象:AWS provider not authorized to perform iam:CreateRole 或 vapor deploy production fails with "AccessDenied"——不是密钥没填对,而是你给 CLI 用的 IAM 用户缺权限。
- 必须授予的最小策略:
AdministratorAccess-AWSElasticBeanstalk+AWSServiceRoleForElasticBeanstalk(Vapor 内部仍依赖 EB 的底层能力) -
aws configure用的不是 root 账号,也不是 EC2 实例角色,而是独立 IAM 用户的AWS_ACCESS_KEY_ID和AWS_SECRET_ACCESS_KEY - Vapor CLI 安装后记得加到 PATH:
export PATH="$PATH:$HOME/.composer/vendor/bin",否则vapor login会报 command not found
手动部署到 EC2 时,php artisan storage:link 通常失效
因为 Laravel 默认用 public/storage 指向 storage/app/public,但在 EC2 上 nginx 的 root 往往设为 /var/www/html/project/public,而部署脚本(如 Forge 或自写 deploy.sh)常把整个项目目录 rsync 过去,导致软链接断开或权限被重置。
使用场景:你坚持用传统服务器模型,要跑 Horizon、WebSockets、自定义 cron,或者有文件上传回调逻辑强依赖本地磁盘路径。
- 别在本地生成软链接再上传——EC2 上
storage目录属主是ec2-user,但 nginx 进程跑在apache或www-data下,权限不一致 - 改用
ln -sf /var/www/html/project/storage/app/public /var/www/html/project/public/storage,并在部署后chown -R www-data:www-data /var/www/html/project/storage - 更稳妥的做法:在
config/filesystems.php中把public磁盘的root改成绝对路径,绕过软链接:'root' => '/var/www/html/project/storage/app/public'
doesObjectExist() 返回 false 不代表文件真不存在
这是 S3 SDK 常见的“幽灵失败”:明明控制台里文件清清楚楚,$s3->doesObjectExist($bucket, 'path/file.jpg') 却返回 false。根本原因不是网络或鉴权,而是 Key 名称里的空格、中文、特殊字符没做 URL 编码。
参数差异:S3 控制台显示的 “文件名” 是解码后的友好视图,但 SDK 的 Key 字段必须是原始编码字节串。比如 temp/测试.png 在 SDK 里得传 temp/%E6%B5%8B%E8%AF%95.png。
- 上传时用
rawurlencode()处理 Key:'Key' => 'temp/' . rawurlencode($filename) - 查询前也必须同样编码,否则
doesObjectExist和getObject都会静默失败 - 注意:Laravel 的
Storage::disk('s3')->exists()内部已自动处理编码,但直接调 SDK 客户端就得自己来
别在 .env 里硬写 AWS_SECRET_ACCESS_KEY
哪怕你只在 EC2 上部署,也别把密钥塞进 .env 文件。AWS 推荐且 Vapor/EB 默认采用的方式是:让实例角色(IAM Role)接管权限,代码里完全不碰密钥字符串。
性能 / 兼容性影响:硬编码密钥会导致部署包体积膨胀、Git 历史泄露风险、无法动态轮换;而实例角色由 AWS STS 自动刷新临时凭证,aws-sdk-php 会优先读取 http://169.254.169.254/latest/meta-data/iam/security-credentials/,比读文件还快。
- EC2 启动时绑定一个带
s3:GetObject、s3:PutObject权限的 IAM Role -
.env中删掉AWS_ACCESS_KEY_ID和AWS_SECRET_ACCESS_KEY,只留AWS_BUCKET和AWS_DEFAULT_REGION - 验证是否生效:在服务器上运行
aws s3 ls s3://your-bucket-name,能列出内容就说明角色权限通了
真正麻烦的从来不是“怎么部署”,而是部署后某天凌晨三点发现 S3 图片批量 403——十有八九是密钥轮换没同步,或者忘了删掉测试环境里那行 AWS_SECRET_ACCESS_KEY=xxx。









