Enabling pg_stat_statements
pg_stat_statements is a PostgreSQL extension that tracks execution statistics for every SQL statement run against the server. Query Doctor relies on it to know what queries your application is actually running — without it, there’s nothing to analyze.
Already enabled?
Section titled “Already enabled?”Before going through the setup, check if it’s already there:
SELECT count(*) FROM pg_stat_statements;If you get a number back, you’re all set. If you see relation "pg_stat_statements" does not exist, keep reading.
Self-managed PostgreSQL
Section titled “Self-managed PostgreSQL”1. Edit postgresql.conf
Section titled “1. Edit postgresql.conf”Add pg_stat_statements to the shared preload libraries and turn on tracking:
shared_preload_libraries = 'pg_stat_statements'pg_stat_statements.track = allSetting track = all makes sure statements inside functions and nested calls get tracked too, not just top-level queries.
2. Restart PostgreSQL
Section titled “2. Restart PostgreSQL”This setting requires a full server restart (a reload won’t do it):
# systemdsudo systemctl restart postgresql
# pg_ctlpg_ctl restart -D /var/lib/postgresql/data3. Create the extension
Section titled “3. Create the extension”Connect to your database and run:
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;Using a database migration
Section titled “Using a database migration”If your project uses an ORM or migration tool, putting CREATE EXTENSION in a migration is a good way to make sure the extension is always there — it becomes part of your schema history and runs automatically in every environment.
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;This works well for the CREATE EXTENSION step, but keep in mind that shared_preload_libraries still needs to be configured at the server level (via postgresql.conf, Docker flags, or your provider’s settings). The migration handles the SQL side; the infrastructure handles the server config.
Docker
Section titled “Docker”The official Postgres Docker image lets you pass -c flags directly to the server process, so you don’t need to touch any config files. There are two parts to this:
- Server flags — tell Postgres to load the extension on startup
CREATE EXTENSION— register it in your database so you can actually query it
docker run
Section titled “docker run”docker run -d \ --name postgres \ -e POSTGRES_PASSWORD=postgres \ -p 5432:5432 \ postgres:17 \ -c shared_preload_libraries=pg_stat_statements \ -c pg_stat_statements.track=allThen create the extension:
docker exec -it postgres psql -U postgres -c "CREATE EXTENSION IF NOT EXISTS pg_stat_statements;"docker-compose.yml
Section titled “docker-compose.yml”services: postgres: image: postgres:17 environment: POSTGRES_PASSWORD: postgres ports: - "5432:5432" command: [ "-c", "shared_preload_libraries=pg_stat_statements", "-c", "pg_stat_statements.track=all", ] volumes: - pgdata:/var/lib/postgresql/data
volumes: pgdata:After docker compose up, create the extension:
docker compose exec postgres psql -U postgres -c "CREATE EXTENSION IF NOT EXISTS pg_stat_statements;"Dockerfile
Section titled “Dockerfile”If you want the extension created automatically on first boot (no manual step), you can add an init script to a custom image:
FROM postgres:17
CMD ["-c", "shared_preload_libraries=pg_stat_statements", "-c", "pg_stat_statements.track=all"]
RUN echo "CREATE EXTENSION IF NOT EXISTS pg_stat_statements;" > /docker-entrypoint-initdb.d/pg_stat_statements.sqlThe official Postgres image runs any .sql files in /docker-entrypoint-initdb.d/ the first time it initializes the data directory.
Managed providers
Section titled “Managed providers”Amazon RDS / Aurora
Section titled “Amazon RDS / Aurora”The extension ships with RDS but isn’t turned on by default. You’ll need to flip it on in your parameter group:
- In the RDS console, find your DB parameter group
- Set
shared_preload_librariestopg_stat_statements - Reboot the instance
- Connect and run
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
Supabase
Section titled “Supabase”Already enabled on all Supabase projects — nothing to do here.
Available out of the box. Just create the extension:
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;Useful queries
Section titled “Useful queries”Once you’ve got the extension running, here are a couple of handy queries.
Top queries by total execution time
Section titled “Top queries by total execution time”SELECT queryid, calls, mean_exec_time::numeric(10,2) AS avg_ms, total_exec_time::numeric(10,2) AS total_ms, left(query, 80) AS query_previewFROM pg_stat_statementsORDER BY total_exec_time DESCLIMIT 10;Reset statistics
Section titled “Reset statistics”If things have gotten noisy and you want a clean slate:
SELECT pg_stat_statements_reset();Configuration reference
Section titled “Configuration reference”| Parameter | Default | Description |
|---|---|---|
pg_stat_statements.max | 5000 | Maximum number of statements tracked. Increase if your application runs many distinct queries. |
pg_stat_statements.track | top | Which statements to track. Set to all to include statements inside functions. |
pg_stat_statements.track_utility | on | Whether to track utility commands (e.g. CREATE TABLE, VACUUM). |
pg_stat_statements.track_planning | off | Whether to track planning statistics (adds overhead). |