Rails 项目接 PostgreSQL,真正要动的地方通常只有两处:Gemfile 里的 pg 驱动,和 config/database.yml(或者用 DATABASE_URL 覆盖它)。卡住人的往往不是 yml 怎么写,而是没先确认三件事:PostgreSQL 服务是否已在监听、pg gem 在本机能不能编译、连接用的账号有没有建库建表权限。这三件事任何一条不成立,改 yml 都只是白改。
判断方向:先确认 PostgreSQL 服务可连、pggem 能装上、账号有建库权限,再动database.yml。适用场景是本地开发或自建数据库的常规 Rails 项目;验证信号是rails db:prepare正常结束、ActiveRecord::Base.connection.active?返回 true、psql能看到同名数据库。边界:托管数据库、强制 SSL、连接数受限的环境要按服务方给的参数补sslmode、pool和 URL,不能直接套本地示例。
先确认当前场景和目标
动手前先分清两种场景,处理方式不同:新建项目可以在 rails new myapp -d postgresql 时就选定数据库适配器,生成的 database.yml 已经是 PostgreSQL 结构;已有项目从 SQLite 或 MySQL 迁过来,则要同时改 Gemfile 和 yml,并且原来 db/*.sqlite3 里的数据不会自动搬过去。
需要先确认的信息,建议逐条落实:
- PostgreSQL 是跑在本机、同一台机器的容器里,还是远程实例;主机名和端口(默认 5432)分别是什么。
- 连接方式是 TCP(
host+port)还是本地 Unix socket(host留空或写 socket 目录);两者对应的认证方式在pg_hba.conf里不一样。 - 所用账号是否有
CREATEDB权限,否则rails db:create会失败。 - Rails 版本决定 yml 结构:较新版本默认带
default: &default锚点和primary之类的层级,旧版本是平铺的。拿现有文件改,比凭记忆重写更稳。 - 是否有环境变量注入(
DATABASE_URL),它会覆盖 yml 中同名配置,容易被忽略。
按这个顺序处理
第一步,在 Gemfile 里确保驱动正确。SQLite 项目要把 gem "sqlite3" 换成 gem "pg",然后执行 bundle install。如果这一步在编译阶段报错,通常缺的是 PostgreSQL 客户端开发库,Debian/Ubuntu 系可先装 libpq-dev,macOS 用 Homebrew 安装 postgresql 后一般会带齐。

# Gemfile
source "https://rubygems.org"
gem "rails"
gem "pg" # 原来可能是 gem "sqlite3"
第二步,改 config/database.yml。下面是一份可替换的通用骨架,host、username、password、database 按实际环境替换;本地 socket 连接时把 host 和 port 两行删掉即可。不要把真实密码提交进仓库,建议走环境变量。
default: &default
adapter: postgresql
encoding: unicode
host: <%= ENV.fetch("PGHOST", "127.0.0.1") %>
port: <%= ENV.fetch("PGPORT", 5432) %>
username: <%= ENV.fetch("PGUSER", "postgres") %>
password: <%= ENV.fetch("PGPASSWORD", "") %>
pool: <%= ENV.fetch("RAILS_MAX_THREADS", 5) %>
development:
<<: *default
database: myapp_development
test:
<<: *default
database: myapp_test
production:
<<: *default
database: myapp_production
第三步,建库并跑迁移。顺序建议是 bin/rails db:create 先确认能连上并创建数据库,再 bin/rails db:migrate。Rails 较新版本提供 bin/rails db:prepare 一次性完成建库、加载 schema 和迁移,本地新环境用它更省事。如果部署环境习惯用单个连接串,也可以只设置 DATABASE_URL=postgresql://user:pass@host:5432/myapp_production,再在 yml 里保留 adapter: postgresql 作为适配器声明;两者冲突时以 URL 为准,这点需要结合所用 Rails 版本确认。
改完后怎么验证
不要只看命令退出码,按下面几层依次确认,能更快定位问题出在连接、权限还是迁移。
- 直接用客户端确认服务可达:
psql -h 127.0.0.1 -p 5432 -U postgres -l,能列出数据库说明网络和账号认证都通了。 - 在项目目录执行
bin/rails runner "puts ActiveRecord::Base.connection.active?",输出true表示 Rails 侧连接池已成功建立连接。 - 执行
bin/rails db:migrate:status,能看到迁移列表且状态为 up,说明当前连的是预期数据库,而不是误连到别的库。 - 启动
bin/rails server访问一个会查库的页面,同时观察log/development.log,日志里应出现针对 PostgreSQL 的 SQL 语句;如果日志里仍是 SQLite 语句,多半是 Gemfile 或 yml 没生效,需要检查是否还有残留的sqlite3配置。
预期信号可以归纳为:命令不报连接错误、迁移状态可读、日志出现 PG 风格 SQL。三者缺一,就回到对应层的配置上查,不必继续往下改。

容易忽略的边界
几个常见限制值得提前知道。其一,本地 Unix socket 连接在部分系统上默认走 peer 认证,用系统用户名登录,yml 里写的 username 可能不生效,这时要么改用 TCP 并配置密码认证,要么建立同名数据库角色。其二,pg gem 的编译依赖 libpq,CI 或精简镜像里缺库会直接导致 bundle install 失败,需要在镜像构建阶段补上开发包。
其三,pool 值不是越大越好,它要和应用进程数、数据库允许的最大连接数一起看,超出上限时表现为连接超时而不是明确报错。其四,若项目使用 structure.sql 而非 schema.rb,迁移和加载都依赖 pg_dump,客户端版本与服务器版本差异过大时可能失败,这类环境建议固定客户端版本。其五,托管数据库常要求 SSL,需要在 yml 或 URL 中补 sslmode=require,否则报连接被拒绝但信息不明显。
回退建议:改动前先备份现有的 config/database.yml 和 Gemfile.lock;如果迁移后发现连错库或驱动冲突,先还原这两个文件、执行 bundle install,再逐步定位,不要在多个配置同时改动的情况下排查。对于仍在用 SQLite 且数据量不大的本地项目,短期内保留 SQLite 跑开发、只在需要 PostgreSQL 特性时切换,也是一种可接受的过渡做法,但两套环境的行为差异要自己心里有数。