Neon is a serverless PostgreSQL database that’s great for dynamic workloads like those found in Ruby on Rails applications. Instead of managing persistent database connections, Neon uses a pooled approach where connections are established and torn down as needed, which can be more cost-effective and scalable. Integrating Neon with Rails is pretty straightforward, but there are a few key differences from a traditional PostgreSQL setup you’ll want to be aware of.

Let’s see how this looks in practice. Imagine you have a standard Rails app. You’d typically configure your database connection in config/database.yml. With Neon, it’s similar, but you’ll be using the connection string provided by Neon, which includes more detailed information like the branch, role, and password.

Here’s an example config/database.yml for a Rails app connecting to Neon:

default: &default
  adapter: postgresql
  encoding: unicode
  pool: 5
  host: ep-your-neon-project-id.your-region.aws.neon.tech
  port: 5432
  database: dev
  username: <your_neon_username>
  password: <your_neon_password>

development:
  <<: *default
  url: postgresql://<your_neon_username>:<your_neon_password>@ep-your-neon-project-id.your-region.aws.neon.tech:5432/dev?sslmode=require&options=--c.options%3D--branch%3Dmain

production:
  <<: *default
  url: postgresql://<your_neon_username>:<your_neon_password>@ep-your-neon-project-id.your-region.aws.neon.tech:5432/prod?sslmode=require&options=--c.options%3D--branch%3Dmain

Notice the url configuration. This is where you embed the Neon-specific parameters. The sslmode=require is crucial for secure connections. The options=--c.options%3D--branch%3Dmain part is unique to Neon, telling the connection which branch of your database to use. You’ll get this full connection string from your Neon project dashboard.

The core problem Neon solves for Rails developers is managing database connection overhead and scaling efficiently. Traditional databases often require you to maintain a fixed pool of connections, which can be wasteful if your application’s traffic fluctuates wildly. Neon’s serverless architecture means you only pay for what you use, and connections are managed dynamically. When your app needs to talk to the database, a connection is spun up; when it’s idle, it can be de-provisioned. This is managed by Neon’s compute endpoints.

Internally, Neon works by separating storage and compute. Your data resides in a shared, scalable storage layer. When your Rails app connects, it’s connecting to a compute instance that accesses this data. This isolation allows Neon to scale compute resources independently of storage, making it very flexible. The branching feature, which is heavily used in the database.yml example, allows you to create isolated copies of your database for development, testing, or feature branches without duplicating data.

The pool setting in database.yml still matters, but its interpretation is slightly different. It defines the maximum number of concurrent connections your Rails application will try to maintain to the Neon compute endpoint. Neon’s internal connection pooling will handle the actual establishment and management of connections to the underlying PostgreSQL instances. So, a pool: 5 in Rails means your app will try to keep up to 5 connections open to Neon’s endpoint, and Neon will manage how those translate to actual PostgreSQL connections.

One common point of confusion is how to handle migrations with Neon branches. You can run migrations on a specific branch, and then merge that branch into your main production branch. This allows for isolated development and testing of schema changes. For instance, to create a new table on a feature branch named new-feature:

# Assuming your Rails app is configured to use the 'new-feature' branch
# in database.yml or via environment variables
RAILS_ENV=development bundle exec rails db:create
RAILS_ENV=development bundle exec rails db:migrate

After successful migration and testing on the new-feature branch, you would merge this branch into main within the Neon console, and then update your production database configuration to point to the main branch.

The biggest surprise for many Rails developers is realizing that while Neon feels like a traditional PostgreSQL database, its serverless nature means you don’t have a single, long-lived database server instance to monitor in the same way. The scaling and connection management are abstracted away. This can be a relief for operations but requires a shift in how you think about database performance tuning – it’s less about optimizing your single DB server and more about optimizing your application’s connection patterns and Neon’s compute resource allocation.

When you encounter PG::ConnectionBad: PQconsumeInput() : server closed the connection unexpectedly, it’s often a sign that your Neon compute endpoint has scaled down due to inactivity, and your application is trying to use a stale connection.

Want structured learning?

Take the full Neon course →