Skip to main content
Ruby on Rails is a full-stack web framework whose ORM, Active Record, supports PostgreSQL through the pg gem. In this guide, you create a new Rails app, connect it to ClickHouse Managed Postgres over verified TLS, run a migration, and serve a scaffolded Post resource.

Prerequisites

  • Ruby 3.2 or later. This guide was tested with Ruby 4.0.7, Rails 8.1.4, and pg 1.6.3.
  • A ClickHouse Cloud account.
The pg gem ships precompiled builds for Linux, macOS, and Windows that include libpq, so you don’t need a local PostgreSQL installation.

Create a ClickHouse Managed Postgres service

In the ClickHouse Cloud console, click New service and select Postgres. The instance is ready in a few minutes. See the quickstart for a walkthrough.

Get your connection details

Open your service and click Connect in the left sidebar. Keep Directly selected and turn on Use SSL. Copy the password and server hostname, then click Download CA certificate. The CA certificate is unique to your service. You can also download it later from Settings → CA Certificate. Direct or PgBouncer? A Rails server is a long-lived process that keeps its own connection pool (max_connections in config/database.yml, five per process by default), so this guide connects directly to Postgres on port 5432. If you run many processes and hit the connection limit, see Using PgBouncer.

Create a Rails app

Install Rails and create an app that uses PostgreSQL:
Adding to an existing app?Skip gem install rails and rails new. In your app’s directory, run bundle add pg, then continue with Configure the database connection, where the new development section replaces the SQLite one.

Configure the database connection

Move the CA certificate you downloaded into the app’s config directory and rename it to ca-certificate.pem. The downloaded file is named after your service, for example your-service-ca-certificate.pem:
The CA certificate isn’t a secret, so you can commit it with your app. In config/database.yml, replace the development section with:
sslmode: verify-full makes libpq check that the server certificate is signed by your service’s CA and matches the hostname. Rails.root.join gives an absolute path, so the setting works no matter which directory you start Rails from. Set DATABASE_URL to your service, using a new database name. This guide uses guide_rails:
Leave the query string off DATABASE_URL. Parameters in the URL override the keys in database.yml, so a copied sslrootcert=your-service-ca-certificate.pem would replace your setting with a path relative to the current directory.
Create the database:
db:create only creates the database named in DATABASE_URL. It connects to the default postgres database to run CREATE DATABASE, but doesn’t change it. When DATABASE_URL is set, Rails also skips creating the local test database.

Generate a model and run the migration

Generate a scaffold for a Post resource and apply its migration. In an existing app, bin/rails db:migrate also creates your app’s existing tables in the new database. They start empty, because data in SQLite isn’t copied:

Query the database

Create script/posts_demo.rb with a few Active Record calls:
Run it with bin/rails runner:
The last line reads pg_stat_ssl for the current connection, which confirms that the session is encrypted.

Run the app

Start the development server:
Open http://localhost:3000/posts to list, create, edit, and delete posts. Every request reads from and writes to ClickHouse Managed Postgres. The page lists each post with its title and body and a Show this post link, followed by a New post link. Edit and delete links are on each post’s page. To see the table in the console, open SQL console in the left sidebar, expand the guide_rails database, and select the posts table. Rails also creates schema_migrations and ar_internal_metadata to track migrations. The posts table contains the two rows from script/posts_demo.rb:

Using PgBouncer

Each Rails process opens up to max_connections direct connections. If many processes together approach the Postgres connection limit, connect through the bundled PgBouncer instead: select via PgBouncer in the Connect modal and use port 6432 in DATABASE_URL. PgBouncer runs in transaction pooling mode:
  • Prepared statements work with Rails defaults if the pg gem is 1.6 or later and uses libpq 17 or later. The precompiled pg 1.6 gems bundle libpq 18. With these versions, when Rails evicts a statement from its cache, it releases it with a protocol-level Close message, which the bundled PgBouncer handles. Older pg or libpq versions send SQL DEALLOCATE instead, which fails through PgBouncer with prepared statement "a1" does not exist. If that happens inside a transaction, the transaction is aborted (PG::InFailedSqlTransaction). To check your versions, run:
    You need 1.6.0 or later and 170000 or later. If you can’t upgrade, add prepared_statements: false to the development section. Rails turns prepared statements on in production, but in development they’re off by default because query_log_tags_enabled is on.
  • Migrations take a session-level advisory lock (pg_try_advisory_lock) to prevent concurrent runs. In transaction pooling, the unlock can reach a different Postgres connection, which fails with Failed to release advisory lock and leaves the lock held, so later runs fail with Cannot run migrations because another migration process is currently running. Either run bin/rails db:migrate with a direct DATABASE_URL on port 5432, or turn off the lock:
With advisory_locks: false, make sure only one deploy runs migrations at a time.

Troubleshooting

  • SSL error: certificate verify failed: sslrootcert doesn’t point to your service’s CA certificate. Download it again from Settings → CA Certificate. Each service has its own CA, and public CA bundles don’t work.
  • root certificate file "..." does not exist: the path in sslrootcert is wrong. Check that config/ca-certificate.pem exists.

Next steps

  • Use the same sslmode and sslrootcert settings in your production configuration.
  • Connection: connection strings, PgBouncer, and TLS.
  • Settings: change Postgres and PgBouncer parameters such as max_connections.
  • Read replicas: scale reads, for example with Rails multiple databases.
  • Sync to ClickHouse: replicate your Rails tables to ClickHouse for analytics.
Last modified on September 30, 2026