把一个 Rails 应用从本地开发环境搬到 Debian 服务器上,听起来只是换个地方运行,实际操作时却会踩到各种各样的坑。本地有开发环境帮忙兜底,而生产环境一旦配置不到位,页面直接就是白屏或者 500 错误。这篇文章以一台干净的 Debian 11 系统为例,从零开始走一遍完整的部署流程,重点讲清楚每一步为什么这么做,而不是单纯罗列命令。

一、环境准备:装好 Ruby 和基础依赖
Debian 自带的软件源里有 Ruby 包,但版本通常比较老,而且系统 Ruby 和后续安装的 gems 混在一起容易产生权限问题。生产环境更推荐用 rbenv 或者 RVM 来管理 Ruby 版本,这样每个应用可以有独立的运行环境,升级 Ruby 也不会牵一发而动全身。
先更新系统并安装编译依赖,这些库是编译 Ruby 和安装部分 gem 时必需的:
apt update && apt upgrade -y apt install -y git curl build-essential libssl-dev libreadline-dev \ zlib1g-dev libsqlite3-dev libpq-dev libyaml-dev
然后安装 rbenv 和 ruby-build 插件,再用它装一个稳定的 Ruby 版本。注意安装完成后要按照提示把 eval "$(rbenv init -)" 加进当前用户的 shell 配置文件里,否则重启终端后 rbenv 会失效。
git clone https://github.com/rbenv/rbenv.git ~/.rbenv git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc echo 'eval "$(rbenv init -)"' >> ~/.bashrc source ~/.bashrc rbenv install 3.2.2 rbenv global 3.2.2 gem install bundler
这里有个细节值得注意:libpq-dev 是 PostgreSQL 的开发头文件,如果不装它,后面 bundle install 编译 pg 这个 gem 时会直接报错,错误信息还经常让人摸不着头脑。提前装好能省不少排查时间。
二、应用部署与数据库配置
环境就绪后,把代码拉到服务器上。小项目直接 git clone 就够了,如果是多人协作或者需要频繁更新,建议配置 SSH key 做免密拉取,或者引入 Capistrano 这类部署工具做自动化。代码建议放在 /var/www 目录下,方便统一管理权限。
mkdir -p /var/www/myapp git clone git@your-git-server:you/myapp.git /var/www/myapp cd /var/www/myapp bundle config set --local deployment true bundle install --without development test
上面用了 deployment 模式,它会把 gem 装到应用目录下的 vendor/bundle,隔离性更好,也避免了全局 gem 目录膨胀。--without development test 则跳过开发和测试组的依赖,减小部署体积。
接下来处理数据库。生产环境一般用 PostgreSQL,先在 Debian 上安装并创建专属用户和数据库:
apt install -y postgresql postgresql-contrib sudo -u postgres psql
在 psql 里执行:
CREATE USER myapp WITH PASSWORD '一个强密码'; CREATE DATABASE myapp_production OWNER myapp; \q
然后编辑 config/database.yml,把生产环境的连接信息填进去。密码这类敏感信息不要明文写在代码库里,推荐用环境变量或者 dotenv 这类工具管理。配置完成后执行迁移:
RAILS_ENV=production bundle exec rails db:migrate RAILS_ENV=production bundle exec rails assets:precompile
资产预编译是新手最容易翻车的环节之一。如果应用依赖 Node.js 工具链(比如用了 webpacker 或者 importmap 之外的方案),需要先装好 Node.js。另外 Rails 6 之后要求配置 config/master.key 或者设置 RAILS_MASTER_KEY 环境变量,否则预编译和启动都会报错。
三、用 Nginx 加 Passenger 对外提供服务
Rails 自带的 Puma 服务器性能不差,但直接让它面对公网流量并不是好主意。常见的做法是在前面放一层 Nginx 做反向代理,后面要么用 Passenger 一体化集成,要么用 Puma 加 Unix Socket 手动对接。这里以 Passenger 为例,它的优点是和 Nginx 结合紧密,配置量少,进程管理也省心。
apt install -y dirmngr gnupg apt-transport-https ca-certificates gpg --keyserver keyserver.ubuntu.com --recv-keys 561F9B9CAC40B2F7 gpg --export --armor 561F9B9CAC40B2F7 | apt-key add - sh -c 'echo deb https://oss-binaries.phusionpassenger.com/apt/passenger bullseye main > /etc/apt/sources.list.d/passenger.list' apt update apt install -y nginx libnginx-mod-http-passenger
装完后编辑 Nginx 站点配置,把请求指向 Rails 应用的 public 目录:
server {
listen 80;
server_name your-domain.com;
root /var/www/myapp/public;
passenger_enabled on;
passenger_ruby /home/deploy/.rbenv/shims/ruby;
passenger_app_env production;
}passenger_ruby 这一行很关键,它必须指向实际运行应用的 Ruby 解释器路径。用 rbenv 的话要填 shims 目录下的 ruby,填错了 Passenger 会报找不到 Ruby 的错误。另外还要确认 Nginx 的运行用户对应用目录有读取权限,log/ 和 tmp/ 目录要有写权限,否则会出现启动卡住或 502 的情况。最后重载配置即可:
nginx -t && systemctl reload nginx
四、生产环境收尾与常见坑
服务跑起来只是第一步,生产环境还有几件事必须补上。第一是 secret_key_base,生产环境下它不会自动生成,需要运行 rails secret 拿到一串密钥,写进环境变量或者用 credentials 加密存储。缺少它时 cookie 签名会失败,典型表现是登录功能全挂,日志里反复出现加密相关报错。
第二是日志与进程守护。Rails 的生产日志默认写在 log/production.log,建议配合 logrotate 做轮转防止磁盘被撑爆。Passenger 自带进程管理,Nginx 重启后应用会自动拉起,这一点比手动跑 Puma 省事不少。
第三,部署完成后如果页面还是报错,按这个顺序排查基本能定位问题:先看 Nginx 错误日志 /var/log/nginx/error.log,Passenger 的报错也会打在这里;再看应用日志 log/production.log;最后确认资产是否预编译成功、数据库迁移是否执行过。502 大多是应用进程没起来或者权限问题,500 则多半是配置缺项,比如密钥没配、数据库连不上。
整体来说,Debian 上部署 Rails 的流程并不复杂,关键是理解每个环节的职责:rbenv 管运行时,Bundler 管依赖,PostgreSQL 管数据,Nginx 加 Passenger 管流量分发和进程守护。把这些串起来之后,后续更新代码只需要 git pull、迁移数据库、预编译资产、重载 Nginx 四步,几分钟就能完成一次发布。