
Laravel是东南亚最流行的PHP框架。要在VPS上正确部署它,您需要设置完整堆栈:Nginx + PHP-FPM + MySQL + Composer、配置文件权限、.env和队列工作程序。本指南将您从空白的Ubuntu 22.04 VPS带到正在运行的生产应用 — 一步一步。
推荐的VPS规格:2 GB或更多RAM、30 GB+ SSD、Ubuntu 22.04 LTS,适用于中等规模的Laravel应用。
理解VPS上的Laravel堆栈
在深入了解安装命令之前,了解不同组件如何协同工作会很有帮助。每个用户请求都通过服务器上的多个层:
- Nginx — 接受来自互联网的HTTP/HTTPS连接,充当反向代理,将PHP请求转发到PHP-FPM。
- PHP-FPM(FastCGI流程管理器) — 处理PHP代码并运行Laravel应用逻辑。
- MySQL — 存储所有应用数据:用户、帖子、订单、设置。
- Laravel应用 — 通过
public/index.php接收请求,通过控制器和模型路由它们,然后返回响应。 - 队列工作程序(可选) — 处理大量后台任务,如发送电子邮件、调整图像大小或导出报告,而不阻塞请求周期。
理解这个流程使调试变得容易得多。如果页面无法加载,首先检查Nginx日志。如果看到PHP错误,检查PHP-FPM日志。如果数据未保存,查看MySQL错误。
为什么VPS对Laravel比共享主机更好:共享主机限制PHP扩展,通过终端阻止Artisan命令,并防止运行像队列工作程序这样的持久后台进程。VPS为您提供完全的root访问权限、安装任何扩展的能力、自定义php.ini的能力,以及24小时运行工作程序的能力。
在部署Laravel之前加固VPS
在安装任何软件之前,先保护服务器。连接到互联网的新VPS将在几分钟内面临自动化端口扫描和暴力攻击。
配置UFW防火墙
# 只允许必要的端口
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status创建非Root部署用户
sudo adduser deployer
sudo usermod -aG sudo deployer
# 将您的SSH密钥复制到新用户
sudo rsync --archive --chown=deployer:deployer ~/.ssh /home/deployer禁用SSH上的密码登录
sudo nano /etc/ssh/sshd_config
# 设置这两行:
PasswordAuthentication no
PermitRootLogin no
sudo systemctl restart sshd在禁用密码认证之前,请始终验证基于密钥的登录是否有效,以避免将自己锁定在外。
步骤1 — 更新系统并安装Nginx
# 更新软件包列表 sudo apt update && sudo apt upgrade -y # 安装Nginx sudo apt install nginx -y sudo systemctl enable nginx sudo systemctl start nginx
步骤2 — 安装PHP 8.2 + Laravel扩展
# 添加PHP PPA (Ubuntu 22.04) sudo add-apt-repository ppa:ondrej/php -y sudo apt update # 安装PHP 8.2和所有Laravel需要的扩展 sudo apt install php8.2 php8.2-fpm php8.2-mysql php8.2-xml \ php8.2-curl php8.2-mbstring php8.2-zip php8.2-bcmath \ php8.2-tokenizer php8.2-gd php8.2-intl -y # 验证版本 php -v
步骤3 — 安装MySQL
sudo apt install mysql-server -y
sudo mysql_secure_installation
# 为Laravel创建数据库和用户
sudo mysql -u root -p
CREATE DATABASE laravel_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'laravel_user'@'localhost' IDENTIFIED BY 'StrongPassword123!';
GRANT ALL PRIVILEGES ON laravel_db.* TO 'laravel_user'@'localhost';
FLUSH PRIVILEGES;
EXIT;第四步 — 安装Composer
curl -sS https://getcomposer.org/installer | php sudo mv composer.phar /usr/local/bin/composer composer --version
第五步 — 上传您的Laravel项目
使用Git或SCP将项目传输到VPS:
# 从Git克隆(推荐) cd /var/www sudo git clone https://github.com/yourusername/your-laravel-app.git myapp sudo chown -R www-data:www-data /var/www/myapp # 安装Composer依赖项(生产模式) cd /var/www/myapp sudo -u www-data composer install --no-dev --optimize-autoloader
第六步 — 配置.env和应用程序密钥
cp .env.example .env nano .env # 编辑以下值: APP_ENV=production APP_DEBUG=false APP_URL=https://yourdomain.com DB_DATABASE=laravel_db DB_USERNAME=laravel_user DB_PASSWORD=StrongPassword123! # 生成应用程序密钥 php artisan key:generate # 运行数据库迁移 php artisan migrate --force
第七步 — 设置存储权限
sudo chown -R www-data:www-data /var/www/myapp/storage
sudo chown -R www-data:www-data /var/www/myapp/bootstrap/cache
sudo chmod -R 775 /var/www/myapp/storage
sudo chmod -R 775 /var/www/myapp/bootstrap/cache
# 创建公共存储符号链接
php artisan storage:link第八步 — 配置Nginx虚拟主机
sudo nano /etc/nginx/sites-available/myapp # 粘贴此配置: server { listen 80; server_name yourdomain.com www.yourdomain.com; root /var/www/myapp/public; index index.php; location / { try_files $uri $uri/ /index.php?$query_string; } location ~ \.php$ { fastcgi_pass unix:/run/php/php8.2-fpm.sock; fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; include fastcgi_params; } location ~ /\.ht { deny all; } } # 启用站点 sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/ sudo nginx -t sudo systemctl reload nginx
第九步 — 使用Certbot安装SSL
sudo apt install certbot python3-certbot-nginx -y
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com
# Certbot将自动配置HTTPS和自动续期第十步 — 配置队列工作程序(如果使用队列)
# Create a Supervisor config for the queue worker sudo apt install supervisor -y sudo nano /etc/supervisor/conf.d/laravel-worker.conf [program:laravel-worker] process_name=%(program_name)s_%(process_num)02d command=php /var/www/myapp/artisan queue:work --sleep=3 --tries=3 autostart=true autorestart=true user=www-data numprocs=2 redirect_stderr=true stdout_logfile=/var/www/myapp/storage/logs/worker.log # Reload Supervisor sudo supervisorctl reread sudo supervisorctl 更新 sudo supervisorctl start laravel-worker:*
第十一步 — 针对生产环境进行优化
# 缓存配置、路由和视图 php artisan config:cache php artisan route:cache php artisan view:cache # 优化Composer自动加载器 composer dump-autoload --optimize
使用Cron设置Laravel调度器
Laravel内置的任务调度器让您可以用PHP代码管理所有计划任务,而无需创建多个独立的Cron作业。服务器上只需要一条Cron条目:
# 编辑www-data用户的crontab sudo crontab -u www-data -e # 添加此单行以每分钟运行调度器 * * * * * cd /var/www/myapp && php artisan schedule:run >> /dev/null 2>&1
配置完成后,应用程序调度器中定义的每个任务(每日邮件、每周清理、每小时数据同步)都将在正确的时间自动触发。示例任务:
# routes/console.php (Laravel 10+)
use Illuminate\Support\Facades\Schedule;
Schedule::command('emails:send-digest')->dailyAt('08:00');
Schedule::command('logs:clean')->weekly();
Schedule::command('cache:prune-stale-tags')->hourly();监控和调试Laravel部署
上线后,监控确保应用程序保持健康状态。Laravel的日志系统默认将所有错误写入storage/logs/laravel.log。
实时查看日志
tail -f /var/www/myapp/storage/logs/laravel.log
常见问题与解决方法
| 症状 | 可能原因 | 解决方法 |
|---|---|---|
| 500 服务器内部错误 | 权限错误或缺少.env值 | 检查storage/权限和.env是否完整 |
| 404 页面未找到 | Nginx根目录或try_files配置错误 | 确认根路径指向public/ |
| 502 网关错误 | PHP-FPM未运行 | sudo systemctl restart php8.2-fpm |
| 队列中任务卡住 | Supervisor未运行 | sudo supervisorctl start laravel-worker:* |
在VPS上管理多个环境
生产环境的Laravel应用通常涵盖至少三个环境:本地(开发者机器)、预发布(QA测试)和生产(线上服务器)。保持环境隔离可防止测试数据污染真实数据,并让团队在更改到达用户之前安全验证。
环境文件结构
# 如何组织环境文件
.env # 当前配置(永不提交 — 已列入.gitignore)
.env.example # 开发者入职模板(提交此文件)
.env.staging # 预发布值(仅服务器端,不提交)
.env.production # 生产值(仅服务器端,不提交)
结构良好的生产环境.env
APP_NAME="MyApp"
APP_ENV=production
APP_DEBUG=false
APP_URL=https://yourdomain.com
LOG_CHANNEL=daily
LOG_LEVEL=error
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel_prod
DB_USERNAME=laravel_prod_user
DB_PASSWORD=VeryStrongProductionPassword!
CACHE_DRIVER=redis
SESSION_DRIVER=redis
QUEUE_CONNECTION=redis
黄金法则:每个.env文件必须列入.gitignore。只有.env.example(不含真实机密)才应提交。泄露的生产环境.env会立即暴露数据库凭据和加密密钥。
Laravel Horizon:VPS上的队列仪表板
Laravel Horizon是由Laravel团队构建的官方队列监控仪表板。它提供作业吞吐量、处理时间、失败率和工作程序负载均衡的实时可见性,使队列调试比阅读原始日志文件快得多。
安装Laravel Horizon
composer require laravel/horizon
php artisan horizon:install
在config/horizon.php中配置工作程序
'environments' => [
'production' => [
'supervisor-1' => [
'maxProcesses' => 10,
'balanceMaxShift' => 1,
'balanceCooldown' => 3,
],
],
'local' => [
'supervisor-1' => [
'maxProcesses' => 3,
],
],
],
Horizon的Supervisor配置
sudo nano /etc/supervisor/conf.d/laravel-horizon.conf
[program:laravel-horizon]
process_name=%(program_name)s
command=php /var/www/myapp/artisan horizon
autostart=true
autorestart=true
user=www-data
redirect_stderr=true
stdout_logfile=/var/www/myapp/storage/logs/horizon.log
stopwaitsecs=3600
sudo supervisorctl reread && sudo supervisorctl update
sudo supervisorctl start laravel-horizon
限制Horizon仪表板访问
// app/Providers/HorizonServiceProvider.php
protected function gate(): void
{
Gate::define('viewHorizon', function ($user) {
return in_array($user->email, [
'[email protected]',
]);
});
}
配置完成后,在https://yourdomain.com/horizon访问Horizon仪表板。您可以查看待处理、处理中和失败的作业,并直接从界面重试失败的作业,无需SSH访问。
速率限制与API滥用防护
生产应用需要防范机器人和过度请求API的客户端。Laravel通过throttle中间件内置速率限制,并以Redis为后端,实现跨多个队列工作程序或进程的分布式计数。
在RouteServiceProvider中定义速率限制
// In App\Providers\RouteServiceProvider
RateLimiter::for('api', function (Request $request) {
return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
});
RateLimiter::for('login', function (Request $request) {
return [
Limit::perMinute(5)->by($request->input('email')),
Limit::perMinute(20)->by($request->ip()),
];
});
添加Nginx级别速率限制作为第一道防线
# In /etc/nginx/nginx.conf, http block
limit_req_zone $binary_remote_addr zone=api:10m rate=60r/m;
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;
# In your Laravel server block
location /api/ {
limit_req zone=api burst=20 nodelay;
limit_req_status 429;
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
}
location /login {
limit_req zone=login burst=3 nodelay;
limit_req_status 429;
try_files $uri $uri/ /index.php?$query_string;
}
| 层级 | 工具 | 优势 |
|---|---|---|
| 网络层 | UFW 防火墙 | 在流量到达Nginx前阻断端口 |
| Web服务器层 | Nginx limit_req | 在调用PHP前进行速率限制 |
| 应用层 | Laravel throttle | 基于Redis跟踪的每用户限制 |
| 进程层 | Fail2ban | 自动封禁触发过多429错误的IP |
在Nginx和Laravel两层同时进行防护(纵深防御),意味着恶意客户端在PHP-FPM被调用之前就会被拒绝,从而在流量高峰期节省CPU和内存资源。
零停机代码更新
将新代码推送到生产环境时,请按照以下顺序操作,以避免在更新窗口期间用户遭遇错误:
# 1. 启用维护模式 php artisan down --retry=60 # 2. 拉取最新代码 git pull origin main # 3. 安装或更新依赖项 composer install --no-dev --optimize-autoloader # 4. 运行待处理的迁移 php artisan migrate --force # 5. 清除并重建缓存 php artisan optimize:clear php artisan optimize # 6. 重启队列工作程序 sudo supervisorctl restart laravel-worker:* # 7. 将站点恢复上线 php artisan up
在运行迁移前使用php artisan down,可防止用户在结构性变更(如添加或删除列)期间遭遇数据库错误。
上线前检查清单:APP_DEBUG=false ✓ → APP_ENV=production ✓ → SSL已激活 ✓ → 存储权限 ✓ → 队列工作程序运行中 ✓ → 配置缓存 ✓ → Cron作业已设置 ✓ — 已准备好投入生产。
⚠️ 安全提醒:永远不要将.env文件提交到Git。启用UFW关闭不必要的端口,并考虑更改默认SSH端口以减少暴力攻击风险。