Rails
Rails uses the standard pg gem and ActiveRecord’s PostgreSQL adapter. The current DB9 deployment
blocks connection initialization with Rails 8.1.4, as described below.
Prerequisites
Section titled “Prerequisites”- A DB9 database (create one)
- Ruby 3.1+
- Rails 7+
Create a DB9 Database
Section titled “Create a DB9 Database”db9 create --name rails-appdb9 create prints the connection string, with the admin password, once — copy it from the Connection String: line. It has this shape:
postgresql://YOUR_TENANT_ID.admin:YOUR_PASSWORD@pg.d0000.db9.io:5432/postgresYOUR_TENANT_ID is the tenant ID prefix in the generated connection string. Copy the username from the Connection String: line printed by db9 create or from db9 db connect. The examples below use pg.db9.io:5433, which reaches the same server; either host and port works. db9 db status rails-app shows the connection string without the password; if you lose it, db9 db reset-password rails-app sets a new password and prints a new connection string.
Settings that take the password on its own need it unencoded: the connection string URL-encodes it (%2B is +, %3D is =), while db9 create --show-password and db9 db reset-password also print it as plain text on the Admin Pass: line.
Set the connection string as an environment variable:
export DATABASE_URL="postgresql://YOUR_TENANT_ID.admin:YOUR_PASSWORD@pg.db9.io:5433/postgres?sslmode=require"Create a New Rails App
Section titled “Create a New Rails App”rails new rails-db9 --database=postgresqlcd rails-db9The --database=postgresql flag adds the pg gem to your Gemfile and generates the correct config/database.yml template.
Configure Database Connection
Section titled “Configure Database Connection”The simplest approach is to use the DATABASE_URL environment variable. Rails will pick it up automatically.
To configure directly, edit config/database.yml:
default: &default adapter: postgresql host: pg.db9.io port: 5433 database: postgres username: YOUR_TENANT_ID.admin password: <%= ENV["DB9_PASSWORD"] %> sslmode: require pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> timeout: 5000
development: <<: *default
test: <<: *default
production: <<: *default pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 10 } %>Define Models
Section titled “Define Models”Generate a User model:
rails generate model User name:string email:stringGenerate a Post model with a foreign key to User:
rails generate model Post title:string body:text published:boolean user:referencesEdit the generated models to add validations and associations:
class User < ApplicationRecord has_many :posts, dependent: :destroy
validates :name, presence: true validates :email, presence: true, uniqueness: trueendclass Post < ApplicationRecord belongs_to :user
validates :title, presence: trueendRun Migrations
Section titled “Run Migrations”rails db:migrateControllers and Routes
Section titled “Controllers and Routes”Generate a Users controller:
rails generate controller UsersAdd a simple CRUD controller:
class UsersController < ApplicationController def index @users = User.all render json: @users end
def show @user = User.find(params[:id]) render json: @user, include: :posts end
def create @user = User.new(user_params) if @user.save render json: @user, status: :created else render json: { errors: @user.errors }, status: :unprocessable_entity end end
def update @user = User.find(params[:id]) if @user.update(user_params) render json: @user else render json: { errors: @user.errors }, status: :unprocessable_entity end end
def destroy User.find(params[:id]).destroy head :no_content end
private
def user_params params.require(:user).permit(:name, :email) endendWire up the routes:
Rails.application.routes.draw do resources :usersendStart the server and test:
rails servercurl http://localhost:3000/usersProduction Notes
Section titled “Production Notes”- Host and port:
pg.db9.io:5433and thepg.d0000.db9.io:5432thatdb9 createprints reach the same server; port5432works on either host. - TLS: Always use
sslmode=requirein your connection string ordatabase.yml. - Connection pool size: Set
poolindatabase.ymlto matchRAILS_MAX_THREADS. Start with 5–10 connections. - Database name: The database is always
postgres. Do not runrails db:create— it will fail because the database already exists and you cannot create new ones. - Puma threads: If using Puma, ensure the pool size in
database.ymlis at least equal toRAILS_MAX_THREADSto avoid connection checkout timeouts.
Troubleshooting
Section titled “Troubleshooting”Connection refused on port 5432
Section titled “Connection refused on port 5432”DB9 accepts port 5432 as well as 5433, so the client is not reaching DB9 at all. A refusal from localhost or 127.0.0.1 means Rails did not get the DB9 host — check that DATABASE_URL is set, or that host is set in database.yml.
rails db:create fails
Section titled “rails db:create fails”The postgres database already exists on DB9. Skip db:create and run db:migrate directly.
ActiveRecord::ConnectionNotEstablished
Section titled “ActiveRecord::ConnectionNotEstablished”Verify that your username follows the format <tenant_id>.admin. Use the complete username from the generated connection string or db9 db connect; rails-app.admin fails even with the right password. DB9 routes pgwire connections by the tenant ID prefix. Also confirm sslmode=require is set.
Migrations fail with permission errors
Section titled “Migrations fail with permission errors”DB9 connects you to the postgres database with admin privileges for your tenant. If you see schema-related errors, confirm you are not trying to modify system tables or create additional databases.
Slow first request
Section titled “Slow first request”The first connection to DB9 may take slightly longer due to TLS handshake and tenant routing. Subsequent requests reuse pooled connections. Preload the connection pool by adding an initializer:
ActiveRecord::Base.connection_pool.checkoutNext Pages
Section titled “Next Pages”- Connect — connection strings and authentication
- Production Checklist — deployment readiness