A Rails Engine that provides a web UI for monitoring and analyzing PostgreSQL query performance. Mount it into your Rails application for easy database introspection and slow query analysis.
- Database Overview: View PostgreSQL version, installed extensions, and table listings
- Query Performance Analysis: Identify slow queries using the
pg_stat_statementsextension - SQLcommenter Support: Parses and displays SQLcommenter tags from queries
- Endpoint and Active Job Breakdowns: Groups query cost by controller/action and by job class, with a per-job drill-down into its query shapes
- Multi-Database Support: Works with multiple database configurations (primary, replica, etc.)
Add this line to your application's Gemfile:
gem "pg_peek"And then execute:
bundle installThen run the installer, which mounts the engine at /pg_peek and creates the
initializer:
bin/rails generate pg_peek:installPass --skip-routes if you would rather mount the engine yourself, for example
inside an authenticated scope.
Now map each database in your database.yml to the ActiveRecord class that
connects to it:
# config/initializers/pg_peek.rb
Rails.application.config.pg_peek.tap do |config|
config.connections = {
"primary" => "ApplicationRecord"
}
endThis step is required. A database without an entry cannot be opened and is not listed.
Visit /pg_peek in your application to access the dashboard.
pg_peek exposes your schema, every normalized query in the database, and an endpoint that resets query statistics. Outside development and test it refuses to serve until you say how it is protected.
Give it credentials and it asks for them over HTTP basic:
PG_PEEK_USERNAME=peek PG_PEEK_PASSWORD=...or equivalently in config/credentials.yml.enc:
pg_peek:
username: peek
password: ...Credentials, once set, apply in every environment — including a development server exposed through a tunnel.
If you would rather mount the engine behind authentication you already have, say so and pg_peek stays out of the way:
# config/initializers/pg_peek.rb
config.public_dashboard = true# config/routes.rb
constraints ->(request) { request.session[:admin_id].present? } do
mount PgPeek::Engine, at: "/pg_peek"
endDevelopment and test are exempt when no credentials are set, so local work is never interrupted.
Query analysis needs the pg_stat_statements extension, and enabling it is two
separate steps on two different things. Doing only the first is the most common
way to end up with a dashboard that shows nothing.
1. Load the module into the server. This is PostgreSQL configuration, not database state: a migration cannot do it, and it takes effect only after a restart.
ALTER SYSTEM SET shared_preload_libraries = 'pg_stat_statements';or the equivalent line in postgresql.conf. Then restart PostgreSQL. On a
managed service this is usually a parameter-group setting; in Docker, pass
-c shared_preload_libraries=pg_stat_statements to the server command.
2. Create the extension in each database. A migration, so it is tracked in your schema and applied per environment:
class EnablePgStatStatements < ActiveRecord::Migration[8.1]
def change
enable_extension "pg_stat_statements"
end
endFor a secondary database, generate it into that database's migrations path:
bin/rails generate migration EnablePgStatStatements --database analyticsIf you do step 2 without step 1, the extension reports itself as installed but its views cannot be queried. pg_peek detects this and shows the instructions above in place of every page that needs statistics, so you will not be left guessing — but you will be left waiting for a restart.
A restart also clears all collected statistics, so expect an empty dashboard until your application has run for a while afterwards.
- Rails 8.1+
- PostgreSQL 14+ with
pg_stat_statementsextension (for query analysis features) - Only officially supported PostgreSQL versions are supported
pg_peek reads pg_stat_statements only. This is deliberate, not an omission.
Percona's pg_stat_monitor has two genuine advantages: it parses SQLcommenter
tags into a column of their own, and it keeps time-bucketed history rather than
one counter since the last reset. Both would simplify this engine.
But it is not part of PostgreSQL's contrib, so it is unavailable on Heroku and
most managed PostgreSQL. And its bucketed figures are not comparable with
pg_stat_statements' cumulative ones: supporting both would give two different
answers to "what does this endpoint cost" depending on which extension happened
to be installed. One data source that works everywhere beats two that disagree.
- Ruby (see
.ruby-version) - Docker and Docker Compose (for PostgreSQL)
# Install dependencies
bundle install
# Start PostgreSQL 18 (default for development)
docker compose up postgres18 -d
# Setup the dummy app database
bin/rails db:setup
# Start the development server
bin/rails s
# Visit http://localhost:3000/pg_peek# Run databases
docker compose up -d
# PostgreSQL 14
bin/test-pg14
# PostgreSQL 15
bin/test-pg15
# PostgreSQL 16
bin/test-pg16
# PostgreSQL 17
bin/test-pg17
# PostgreSQL 18
bin/test-pg18# Run the full test suite
bin/test
# Code style check
bin/rubocopThe gem is available as open source under the terms of the MIT License.