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 install
Then run the installer, which mounts the engine at /pg_peek and creates the
initializer:
bin/rails generate pg_peek:install
Pass --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" } end
This 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" end
Development 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 end
For a secondary database, generate it into that database's migrations path:
bin/rails generate migration EnablePgStatStatements --database analytics
If 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/rubocop
The gem is available as open source under the terms of the MIT License.