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
pg1.6.3. - A ClickHouse Cloud account.
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:Configure the database connection
Move the CA certificate you downloaded into the app’sconfig directory and rename it to ca-certificate.pem.
The downloaded file is named after your service, for example your-service-ca-certificate.pem:
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.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 aPost 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
Createscript/posts_demo.rb with a few Active Record calls:
bin/rails runner:
pg_stat_ssl for the current connection, which confirms that the session is encrypted.
Run the app
Start the development server: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 tomax_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
pggem is 1.6 or later and useslibpq17 or later. The precompiledpg1.6 gems bundlelibpq18. With these versions, when Rails evicts a statement from its cache, it releases it with a protocol-levelClosemessage, which the bundled PgBouncer handles. Olderpgorlibpqversions send SQLDEALLOCATEinstead, which fails through PgBouncer withprepared statement "a1" does not exist. If that happens inside a transaction, the transaction is aborted (PG::InFailedSqlTransaction). To check your versions, run:You need1.6.0or later and170000or later. If you can’t upgrade, addprepared_statements: falseto thedevelopmentsection. Rails turns prepared statements on inproduction, but indevelopmentthey’re off by default becausequery_log_tags_enabledis 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 withFailed to release advisory lockand leaves the lock held, so later runs fail withCannot run migrations because another migration process is currently running. Either runbin/rails db:migratewith a directDATABASE_URLon port5432, or turn off the lock:
advisory_locks: false, make sure only one deploy runs migrations at a time.
Troubleshooting
SSL error: certificate verify failed:sslrootcertdoesn’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 insslrootcertis wrong. Check thatconfig/ca-certificate.pemexists.
Next steps
- Use the same
sslmodeandsslrootcertsettings in yourproductionconfiguration. - 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.