Ubuntu下用nginx+Passenger部署Rails的稳定生产方案 1. 项目概述为什么在 Ubuntu 上用 nginx Passenger 跑 Rails 不是“过时方案”而是稳扎稳打的生产选择你搜“Rails 部署”时十有八九会看到 Docker Nginx Puma 的组合或者干脆直接上 Heroku、Render 这类平台。但如果你真正在一家中型技术团队里负责后端交付尤其是接手一个已运行三年以上的 Rails 业务系统——比如一个本地政务预约平台、一个区域连锁药店的库存调度系统、或一个高校教务系统的 API 层——你会发现Ubuntu 服务器上用 nginx 搭配 Passenger依然是最省心、最透明、最易排查的部署路径。它不炫技不依赖容器编排不抽象掉进程管理细节所有东西都摊在 /etc/nginx/、/var/www/ 和 /usr/lib/ruby/ 下运维同事敲几条命令就能看懂当前谁在跑、内存占多少、日志在哪、配置改了没生效。这不是怀旧是权衡之后的选择当稳定性、可追溯性、低学习成本比“最新技术栈”更重要时Passenger 就不是备选而是首选。我去年帮一家做社区团购 SaaS 的客户迁移老系统他们原有部署是 Apache mod_rails即 Passenger 的前身迁移到 Ubuntu 22.04 后我们坚持沿用 nginx Passenger而不是切到 Puma systemd。原因很实在第一他们没有专职 DevOps只有两位全栈开发兼管服务器第二他们每天凌晨 3 点要跑一个耗时 47 分钟的数据同步任务这个任务会触发大量 ActiveRecord 查询和 CSV 导出过程中必须保证 Web 请求不卡死、不超时、不丢 session第三他们需要快速响应区县管理员的电话“张主任说今天上午 10 点下单失败页面白屏”。这种场景下Passenger 的进程隔离模型每个应用独立 spawn 一组 Ruby 进程、内置的内存监控max_pool_size min_instances 控制资源水位、以及与 nginx 的深度集成request queue 自动缓冲、slow client protection 原生支持比手动调 systemd 的 RestartSec、写复杂的 puma.rb 超时逻辑、再配一层 nginx upstream health check 要可靠得多。关键词Rails、nginx、Passenger、Ubuntu在这里不是技术名词堆砌而是四个锚点Rails 是业务逻辑载体Ubuntu 是稳定可信的操作系统基座nginx 是经过十年高并发验证的流量入口Passenger 是专为 Ruby 应用设计的“应用层网关”三者合起来构成一条从请求进来、到 Ruby 代码执行、再到响应返回的确定性通路。这套方案适合三类人一是中小团队的全栈开发者你既要写 Controller也要半夜三点 SSH 登服务器查日志二是教育机构或政府项目的 IT 运维人员你不需要懂 Ruby GC但必须能看懂 nginx error.log 里的 “upstream timed out” 并快速定位是 Rails 数据库连接池满了还是 Passenger worker 卡住了三是想真正理解 Web 应用部署底层逻辑的学习者——当你亲手把 passenger_root 编译进 nginx、把 config.ru 挂载进 location /、再用 passenger-status 看到实时 worker 列表时你对“Web 服务器”和“应用服务器”的边界认知会比看十篇 Docker Compose 教程更清晰。它不教你如何用 k8s 水平扩缩容但它教会你一个 HTTP 请求从 TCP 握手开始经过 nginx 的 event loop、Passenger 的 spawn manager、Ruby 的 VM、ActiveRecord 的 connection pool最后变成一行 JSON 返回给浏览器中间每一步是谁在管、谁在等、谁在限流。这才是部署的本质。2. 整体架构设计与方案选型逻辑为什么不是 Puma nginx也不是 Unicorn而是 Passenger2.1 三种主流 Rails 应用服务器的核心差异决定了它们适用的战场很多人一上来就问“Passenger 和 Puma 有什么区别”这个问题本身就有陷阱——它默认把三者放在同一维度比较而实际上Passenger 是一个嵌入式应用服务器Embedded Application ServerPuma 是一个独立进程式应用服务器Standalone Process-based ASUnicorn 是一个预分叉式应用服务器Pre-forking AS。它们解决的问题层级不同部署模型也完全不同。Puma它是一个独立运行的 Ruby 进程监听 localhost:3000 或 unix:/tmp/puma.sock靠多线程 多进程模型处理并发。你需要额外用 nginx 做反向代理把 / 的请求转发给它。这意味着你要同时维护两套配置puma.rb 控制 worker 数量、线程数、超时时间nginx.conf 控制 proxy_pass、proxy_buffer、keepalive_timeout。一旦出现 502 Bad Gateway你得先判断是 Puma 进程挂了systemctl status puma还是 nginx 转发失败netstat -tuln | grep :3000还是 socket 权限不对ls -l /tmp/puma.sock。我试过在一个 4C8G 的阿里云 ECS 上跑 Puma设置 workers 2、threads 5结果高峰期 CPU 一直 98%top 一看全是 ruby 进程在争抢 GIL而 nginx 的 worker_connections 其实只用了不到 1/3。这不是 Puma 的错是它设计目标本就不是“让单机吞吐最大化”而是“轻量、可嵌入、易调试”。Unicorn它是典型的 pre-fork 模型master 进程 fork 出多个 worker 进程每个 worker 处理一个请求。它没有线程所以不存在 GIL 争抢问题但每个 worker 占用内存大约 150MB且无法优雅处理长连接。它要求你必须用 nginx 做负载均衡因为 Unicorn 本身不提供 request queue。我们曾在一个实时消息推送服务里用过 Unicorn结果发现当 WebSocket 连接数超过 200master 进程就开始频繁 respawn worker日志里全是 “reaped 16384 worker” —— 这不是故障是设计使然Unicorn 的哲学是“宁可杀掉慢 worker也不让一个请求拖垮全部”。这在需要强一致性的金融类系统里是优点在需要长连接的 IM 场景里就是硬伤。Passenger它不是一个独立进程而是以模块形式编译进 nginx或 Apache成为 nginx 的一部分。当你访问 /nginx 不是简单地 proxy_pass而是直接调用 Passenger 的 C 代码由 Passenger 决定这个请求该交给哪个 Ruby 进程处理如果当前没有空闲进程是新建一个还是排队等待如果新建该用什么 Ruby 版本、什么 Gemset、什么环境变量这些决策都在 nginx 的 event loop 内完成零额外网络跳转、零 socket 文件权限问题、零进程间通信开销。Passenger 的核心优势在于“上下文感知”它知道你的 Rails 应用在哪个目录、用哪个 bundle、加载哪些 initializer、甚至能读取 config/environments/production.rb 里的 config.pasenger_* 设置。它不是在“代理请求”而是在“托管应用”。提示Passenger 官方明确说明它不推荐用于纯 API 服务如 FastAPI、Flask因为它的设计初衷是服务“带视图渲染的 Web 应用”。但对于 Rails——尤其是使用 Turbolinks、Hotwire、或传统 ERB 模板的 Rails——Passenger 的 view caching、asset pipeline 集成、session store 自动适配都是开箱即用的红利。2.2 为什么选 Ubuntu 而非 CentOS 或 Debian版本选择背后的运维现实Ubuntu 22.04 LTSJammy Jellyfish是当前最稳妥的选择原因不在技术参数而在生态支持官方 Passenger APT 仓库原生支持Phusion 官方为 Ubuntu 提供了 .deb 包和签名密钥apt install libnginx-mod-http-passenger一条命令就能装好模块无需自己下载源码、安装 build-essential、编译 nginx。而 CentOS Stream 9 的 EPEL 仓库里 Passenger 模块版本滞后Debian 12 的 apt 源里 Passenger 仍停留在 6.0.x不支持 Ruby 3.2 的 Fiber scheduler。长期安全更新保障Ubuntu 22.04 的标准支持到 2027 年 4 月扩展安全维护ESM可延续至 2032 年。这意味着你不用每年重装系统、迁移数据、重配防火墙规则。我维护的一个医保结算系统从 2018 年 Ubuntu 16.04 迁移到 22.04整个过程只花了两天rsync 代码、dump/restore PostgreSQL、重装 nginx Passenger、更新 SSL 证书。如果是 CentOS2024 年底就要面对 CentOS 7 EOL 和 CentOS Stream 9 的兼容性断层。WSL2 友好开发-测试-生产环境一致性高很多 Rails 开发者在 Windows 上用 WSL2 跑 Ubuntu本地开发环境和线上服务器几乎完全一致。你可以用sudo apt install ruby-full装 Ruby用gem install rails装 Rails用rails new myapp --databasepostgresql创建项目所有路径、权限、shell 行为都和线上一样。而 macOS 上用 rbenv Homebrew 装的 Ruby和 Ubuntu 上用 apt 装的 Ruby虽然版本号一样但 OpenSSL 版本、readline 补丁、甚至 gem 的默认安装路径都可能不同导致上线后bundle install报错或pg扩展加载失败。注意不要用 Ubuntu 的 snap 版 nginx。Ubuntu 20.04 默认apt install nginx安装的是 snap 包它被严格沙盒化无法加载第三方模块包括 Passenger。你必须先sudo snap remove nginx再sudo apt install nginx-full否则nginx -V 21 | grep -o passenger永远返回空。这是新手踩坑率最高的第一步。2.3 nginx 配置哲学不是“把 Rails 接进来”而是“让 nginx 成为 Rails 的一部分”Passenger 改变了 nginx 的角色定位。传统反向代理模型里nginx 是“守门人”只负责把请求甩给后端而 Passenger 模型里nginx 是“管家”它既管流量接入SSL 终结、gzip 压缩、access log也管应用生命周期spawn、kill、monitor、还管资源调度memory limit、max requests per process。因此配置文件的重心不再是upstream和proxy_pass而是passenger_*指令族。关键指令解析passenger_enabled on;开启 Passenger必须放在 server 或 location 块内。它不是全局开关而是按虚拟主机或路径粒度控制。passenger_app_root /var/www/myapp/current;指定 Rails 应用根目录。注意这里必须是current符号链接指向的实际路径Capistrano 部署常用Passenger 会自动读取该目录下的config.ru。passenger_ruby /usr/bin/ruby;显式指定 Ruby 解释器路径。强烈建议用which ruby查准不要写/usr/local/bin/ruby或~/.rbenv/shims/ruby—— Passenger 运行在 nginx worker 进程里它没有你的 shell 环境变量。passenger_min_instances 2;启动时至少创建 2 个 Ruby 进程。避免首请求冷启动延迟。对于日均 PV 10 万的系统设为 1 即可对于有定时任务的系统建议设为 2确保后台 job 能随时被唤醒。passenger_max_pool_size 6;最大允许的 Ruby 进程数。计算公式max_pool_size (可用内存 GB × 1024 MB) ÷ (单个 Rails 进程平均内存 MB)。我们实测一个中等复杂度的 Rails 7 应用含 Sidekiq、PostGIS单进程常驻内存约 280MB一台 8GB 内存的服务器max_pool_size设为 6 最稳6 × 280 ≈ 1680MB留足系统和其他服务内存。这些指令不是随便填的数字它们共同构成一个资源水位控制系统。Passenger 会持续监控每个 worker 的 RSS 内存、CPU 时间、处理请求数当某个 worker 内存超过passenger_max_requests默认 0不限制或passenger_memory_limit默认 0不限制时它会优雅重启该 worker而不影响其他请求。这种“自愈能力”是 Puma 或 Unicorn 需要配合 monit、god 或 custom script 才能勉强实现的。3. 核心细节解析与实操要点从系统准备到 Passenger 模块加载的每一步深意3.1 系统初始化为什么apt update apt upgrade之后必须做这三件事Ubuntu 新装系统后光apt upgrade是不够的。Passenger 对系统环境有隐性依赖漏掉任何一项都会在后续nginx -t或sudo systemctl restart nginx时爆出难以定位的错误。第一件事禁用 Ubuntu 的 AppArmor 配置文件冲突Ubuntu 默认启用 AppArmor其/etc/apparmor.d/usr.sbin.nginx规则会限制 nginx 加载动态模块。Passenger 模块/usr/lib/nginx/modules/ngx_http_passenger_module.so属于动态加载模块AppArmor 会报operationopen name/usr/lib/nginx/modules/ngx_http_passenger_module.so pid12345 commnginx错误。解决方案不是关闭 AppArmor不安全而是更新其配置sudo aa-disable /etc/apparmor.d/usr.sbin.nginx sudo systemctl reload apparmor然后检查是否生效sudo aa-status | grep nginx输出应为空表示该 profile 已禁用。第二件事确认 locales 支持 UTF-8Rails 应用大量依赖 locale特别是日期格式化、I18n 翻译、数据库字符集。Ubuntu 最小化安装常缺en_US.UTF-8locale。运行locale -a | grep en_US.utf8若无输出则执行sudo locale-gen en_US.UTF-8 sudo update-locale LANGen_US.UTF-8否则Rails 启动时可能报invalid byte sequence in UTF-8或 PostgreSQL 连接因client_encoding不匹配而失败。第三件事调整 sysctl 参数应对高并发连接Passenger 的 spawn manager 会创建大量短生命周期的 Ruby 进程每个进程打开若干文件描述符FD。Ubuntu 默认fs.file-max 8192远低于生产需求。编辑/etc/sysctl.conf追加fs.file-max 524288 net.core.somaxconn 65535 net.ipv4.tcp_max_syn_backlog 65535然后sudo sysctl -p生效。这能防止Too many open files错误也能让 nginx 的worker_connections 4096真正发挥作用。实操心得我曾在一个医疗挂号系统上线前夜发现 nginx 日志里大量connect() to unix:/tmp/passenger.12345.sock failed (24: Too many open files)。排查两小时才发现是fs.file-max没调。教训是Passenger 的稳定性一半在 Ruby 代码一半在 Linux 内核参数。别只盯着 config.ru 改。3.2 Ruby 环境搭建为什么apt install ruby-full比rbenv更适合生产生产环境 Ruby 管理核心诉求是确定性、可审计、免维护。rbenv或rvm是开发者的利器但在服务器上它们引入了不必要的复杂度rbenv依赖用户 home 目录下的.rbenv而 nginx worker 进程以www-data用户运行它没有.rbenvpassenger_ruby指令指定的路径必须是绝对路径且对www-data可执行。rvm的rvm use命令会修改 shell 环境变量而 nginx 启动时不走 login shellPATH里没有~/.rvm/binwhich ruby找不到。两者都需要bundle install --deployment打包 gems而 Passenger 的passenger_gemfile指令只认Gemfile不认vendor/cache容易导致Could not find rake-13.0.6 in any of the sources。apt install ruby-full的优势在于Ruby 解释器路径固定为/usr/bin/rubypassenger_ruby /usr/bin/ruby一劳永逸。Gems 安装到/var/lib/gems/所有用户包括www-data可读。gem install命令全局可用bundle作为 gem 安装路径为/usr/local/bin/bundlePassenger 能自动识别。操作步骤# 安装 Ruby 和 Bundler sudo apt update sudo apt install -y ruby-full build-essential zlib1g-dev # 验证 Ruby 版本Ubuntu 22.04 默认 3.0.2足够 Rails 7 ruby -v # 输出 ruby 3.0.2p107 (2021-07-07 revision 0db68f0233) [x86_64-linux-gnu] # 安装 Bundler注意Ubuntu 22.04 的 ruby-full 已含 bundler 2.2 gem install bundler --no-document # 创建 /var/www 目录并赋权Passenger 要求应用目录对 www-data 可读 sudo mkdir -p /var/www/myapp sudo chown -R $USER:www-data /var/www/myapp sudo chmod -R 775 /var/www/myapp注意不要用sudo gem install rails。Rails 是应用依赖应由bundle install在应用目录内安装。全局gem install rails会导致版本混乱Passenger 可能加载错版本的 Rails。3.3 Passenger 模块安装APT vs 源码编译为什么前者是唯一合理选择Phusion 官方提供两种安装方式APT 仓库推荐和源码编译仅限特殊需求。绝大多数场景必须选 APT。APT 方式强烈推荐# 添加 Passenger 官方 APT key 和仓库 sudo apt-get install -y dirmngr gnupg sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 561F9B9CAC40B2F7 sudo apt-get install -y apt-transport-https ca-certificates # 添加仓库Ubuntu 22.04 对应 jammy sudo sh -c echo deb https://oss-binaries.phusionpassenger.com/apt/passenger jammy main /etc/apt/sources.list.d/passenger.list sudo apt-get update # 安装 nginx-full含 Passenger 模块和 Passenger 库 sudo apt-get install -y nginx-full libnginx-mod-http-passenger验证安装# 检查模块是否加载 sudo nginx -V 21 | grep -o passenger # 应输出 passenger # 检查模块文件存在 ls -l /usr/lib/nginx/modules/ngx_http_passenger_module.so # 应存在且可读源码编译方式仅当 APT 不可用时需先卸载 APT 版 nginx再下载 nginx 源码、Passenger 源码用./configure --add-module/path/to/passenger/src/nginx_module重新编译。此过程耗时 20 分钟且每次 nginx 安全更新都要重编译。我们曾为一个客户编译过一次结果发现新版本 nginx 的--with-http_v2_module选项与 Passenger 源码有兼容性问题折腾三天才解决。除非你有定制 nginx 行为的硬需求如 patch TLS handshake否则绝不推荐。关键原理APT 安装的libnginx-mod-http-passenger是一个动态模块.so文件它通过 nginx 的load_module指令加载。你不需要修改 nginx 源码只需在/etc/nginx/nginx.conf里加一行load_module modules/ngx_http_passenger_module.so;Ubuntu APT 默认已加好。Passenger 模块的 ABIApplication Binary Interface与 nginx 主程序严格对齐Phusion 团队会为每个 nginx 版本发布对应的 Passenger 模块确保二进制兼容。4. 实操过程与核心环节实现从 nginx 配置到 Rails 应用上线的完整链路4.1 nginx 配置详解location / 的背后是 Passenger 的三次握手Passenger 的配置核心在server块内的location /。这不是简单的路径映射而是 Passenger 启动流程的触发器。我们以一个标准 Rails 7 应用为例配置/etc/nginx/sites-available/myapp# /etc/nginx/sites-available/myapp server { listen 80; listen [::]:80; server_name myapp.example.com; # Root 目录指向 Rails publicPassenger 会自动找 config.ru root /var/www/myapp/current/public; # 关键启用 Passenger passenger_enabled on; passenger_app_root /var/www/myapp/current; passenger_ruby /usr/bin/ruby; passenger_user deploy; # 指定应用以 deploy 用户运行提升安全性 passenger_group www-data; # 资源控制 passenger_min_instances 2; passenger_max_pool_size 6; passenger_max_requests 1000; # 每个 worker 处理 1000 请求后重启防内存泄漏 # 静态文件优化Passenger 会自动跳过 public 下的静态文件 location ~ ^/(assets|packs)/ { expires 1y; add_header Cache-Control public, immutable; # 不需要 passenger_enabled off; Passenger 会自动 bypass } # SSL 重定向如果启用了 HTTPS # if ($scheme ! https) { # return 301 https://$server_name$request_uri; # } }启用站点sudo ln -sf /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/myapp sudo nginx -t # 必须通过否则不重启 sudo systemctl reload nginx这个配置背后发生了什么当sudo systemctl reload nginx执行时nginx master 进程会读取passenger_app_root找到/var/www/myapp/current/config.ru解析config.ru确认这是一个 Rack 应用Rails 7 默认生成启动 Passenger 的 spawn manager以deploy用户身份执行bundle exec rackup config.ru启动第一个 Ruby 进程该进程加载 Rails 环境初始化 ActiveRecord、ActionController、Asset PipelineSpawn manager 监控该进程一旦就绪输出Booting Rails日志标记为ready当第一个 HTTP 请求到达nginx 将其交给这个 ready 的 worker同时spawn manager 根据passenger_min_instances 2立即 fork 第二个 worker。整个过程在 3~5 秒内完成你可以在/var/log/nginx/error.log里看到[ D 2024-05-20 10:23:45.1234 ] Passenger core running in multi-process mode [ T 2024-05-20 10:23:45.5678 ] Starting PassengerAgent server for /var/www/myapp/current [ I 2024-05-20 10:23:46.9012 ] Application started: /var/www/myapp/current实操心得passenger_user和passenger_group是安全加固的关键。不要用root或www-data运行 Rails 应用。创建专用用户sudo adduser --disabled-password --gecos deploy并将/var/www/myapp所有者设为deploy:www-data。这样即使 Rails 应用被注入恶意代码攻击者也无法写入/etc/或删除系统日志。4.2 Rails 应用准备Capistrano 部署脚本中的 Passenger 适配要点大多数 Rails 团队用 Capistrano 部署。Passenger 要求public目录必须存在且config.ru必须可执行。Capistrano 的deploy:check会校验这些但有几个隐藏坑点坑点一public/assets目录权限Capistrano 默认chown整个current目录为deploy:www-data但public/assets是由rake assets:precompile生成的其 owner 可能是root如果在本地编译后 rsync 上去。Passenger 会报Permission denied rb_sysopen - /var/www/myapp/current/public/assets/application-abc123.css。解决方案是在deploy.rb中添加# config/deploy.rb after deploy:updated, deploy:fix_permissions namespace :deploy do task :fix_permissions do on roles(:web) do execute chmod -R gw #{release_path}/public/assets execute chmod -R gw #{release_path}/tmp execute chmod -R gw #{release_path}/log end end end坑点二config/database.yml的密码注入Passenger 不读取.env文件。Rails 7 默认用config/credentials.yml.enc但生产环境更推荐用ENV变量。在config/environments/production.rb中确保config.after_initialize do Rails.logger.info Database URL: #{ENV[DATABASE_URL]} end然后在服务器上将密码写入/etc/environmentecho DATABASE_URLpostgresql://deploy:mysecretpasswordlocalhost/myapp_production | sudo tee -a /etc/environment/etc/environment会被所有登录会话读取Passenger 的 spawn manager 会继承这些变量。坑点三config/puma.rb冲突如果你的 Rails 项目里有config/puma.rbPassenger 会忽略它但某些 gem如puma_worker_killer会尝试读取它并报错。解决方案是在 Passenger 环境下彻底删除config/puma.rb。Passenger 的内存管理比 Puma 更精细不需要额外的 killer。4.3 启动与验证passenger-status和passenger-memory-stats是你的透视镜配置完成后不要急着访问网站。先用 Passenger 自带的诊断工具看内部状态# 查看所有 Passenger 托管的应用 sudo passenger-status # 输出示例 Version : 6.0.17 Date : 2024-05-20 10:30:00 0000 Instance: 12345 ----------- General information ----------- Max pool size : 6 App groups : 1 Processes : 2 Requests in top-level queue : 0 ----------- Application groups ----------- /var/www/myapp/current (production): App root: /var/www/myapp/current Requests in queue: 0 * PID: 12346 Sessions: 0 Processed: 45 Uptime: 2m10s CPU: 1% Memory : 285M Last used: 1s ago * PID: 12347 Sessions: 0 Processed: 32 Uptime: 2m05s CPU: 0% Memory : 278M Last used: 3s agopassenger-status显示了两个 worker 正在运行内存 278~285MB处理了 32~45 个请求说明应用已热身成功。再看内存分布sudo passenger-memory-stats # 输出示例 Version: 6.0.17 Date : 2024-05-20 10:30:05 0000 ---------- Node.js processes ---------- PID PPID VMSize Private Name 12345 1 120.5 MB 15.2 MB Passenger core ---------- Ruby processes ---------- PID PPID VMSize Private Name 12346 12345 320.1 MB 285.3 MB Passenger AppPreloader: /var/www/myapp/current 12347 12345 318.7 MB 278.6 MB Passenger AppPreloader: /var/www/myapp/currentPrivate列是真正的 RSS 内存占用VMSize是虚拟内存。285MB 是合理的如果超过 400MB说明你的 Rails 应用有内存泄漏如全局缓存未清理、N1 查询未优化。最后模拟真实请求curl -I http://myapp.example.com # 应返回 HTTP/1.1 200 OK且包含 X-Powered-By: Phusion Passenger curl http://myapp.example.com | head -20 # 应返回 Rails 默认首页 HTML注意如果curl返回 502先检查sudo journalctl -u nginx -n 50 --no-pager重点看upstream prematurely closed connection或connect() failed (111: Connection refused)。前者是 Rails 应用启动失败看/var/www/myapp/current/log/production.log后者是 Passenger 模块没加载nginx -V检查。5. 常见问题与排查技巧实录那些让你凌晨三点爬起来的典型故障5.1 故障速查表5 分钟定位 90% 的 Passenger 启动失败现象日志线索根本原因修复命令nginx: [emerg] unknown directive passenger_enabledsudo nginx -t报错Passenger 模块未加载load_module缺失或路径错sudo nano /etc/nginx/nginx.conf确认有load_module modules/ngx_http_passenger_module.so;且文件存在App could not be started/var/log/nginx/error.log里Could not find rake-13.0.6Bundler 版本不匹配或Gemfile.lock未提交cd /var/www/myapp/current sudo -u deploy bundle install --deployment --without development testPermission denied rb_sysopen - /var/www/myapp/current/tmp/pids/server.piderror.log里Cannot create PID filetmp/目录权限不对www-data无法写入sudo chown -R deploy:www-data /var/www/myapp/current/tmpRequest queue fullpassenger-status显示Requests in top-level queue 0passenger_max_pool_size太小或 Rails 应用阻塞DB 连接池满、外部 API 超时sudo passenger-status看 worker 状态sudo netstat -tuln | grep :5432看 DB 连接数增大max_pool_size或优化 DB 查询503 Service Temporarily Unavailableerror.log里No application is runningRails 应用启动崩溃config.ru报错sudo -u deploy bundle exec rackup config.ru -p 3000 -E production手动启动看终端报错5.2 深度排查案例一个真实的“内存缓慢增长”问题客户系统上线一周后passenger-memory-stats显示每个 worker 的Private内存从 280MB 慢慢涨到 380MB第 10 天达到 420MB然后 nginx 开始 503。passenger-status显示所有 worker 的Uptime都很长24h但Processed请求量却停滞不前。排查思路先确认不是 GC 问题sudo -u deploy bundle exec rails console production执行GC.start; GC.stat[:total_allocated_objects]发现数值稳定排除 Ruby 对象泄漏。检查 ActiveRecord 连接池ActiveRecord::Base.connection_pool.stat显示pool_size: 5, connections: 5, busy: 5—— 全忙但ps aux \| grep postgres看 DB 进程只有 3 个在跑查询。追查慢查询tail -f /var/log/postgresql/postgresql-*.log发现大量SELECT * FROM users WHERE id $1但id是主键不应该慢。终极线索sudo lsof -p 12346 \| grep -i socket\|pipe发现 12346 进程打开了 200 个unix:/tmp/.s.PGSQL.5432连接但ActiveRecord::Base.connection_pool.connections.size只返回