Skip to content

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.

Below you’ll find setup instructions for the most common PostgreSQL environments.

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.

Add pg_stat_statements to the shared preload libraries and turn on tracking:

shared_preload_libraries = 'pg_stat_statements'
pg_stat_statements.track = all

Setting track = all makes sure statements inside functions and nested calls get tracked too, not just top-level queries.

This setting requires a full server restart (a reload won’t do it):

Terminal window
# systemd
sudo systemctl restart postgresql
# pg_ctl
pg_ctl restart -D /var/lib/postgresql/data

Connect to your database and run:

CREATE EXTENSION IF NOT EXISTS pg_stat_statements;

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.

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:

  1. Server flags — tell Postgres to load the extension on startup
  2. CREATE EXTENSION — register it in your database so you can actually query it
Terminal window
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=all

Then create the extension:

Terminal window
docker exec -it postgres psql -U postgres -c "CREATE EXTENSION IF NOT EXISTS pg_stat_statements;"
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:

Terminal window
docker compose exec postgres psql -U postgres -c "CREATE EXTENSION IF NOT EXISTS pg_stat_statements;"

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.sql

The official Postgres image runs any .sql files in /docker-entrypoint-initdb.d/ the first time it initializes the data directory.

The extension ships with RDS but isn’t turned on by default. You’ll need to flip it on in your parameter group:

  1. In the RDS console, find your DB parameter group
  2. Set shared_preload_libraries to pg_stat_statements
  3. Reboot the instance
  4. Connect and run CREATE EXTENSION IF NOT EXISTS pg_stat_statements;

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;

Once you’ve got the extension running, here are a couple of handy queries.

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_preview
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 10;

If things have gotten noisy and you want a clean slate:

SELECT pg_stat_statements_reset();
ParameterDefaultDescription
pg_stat_statements.max5000Maximum number of statements tracked. Increase if your application runs many distinct queries.
pg_stat_statements.tracktopWhich statements to track. Set to all to include statements inside functions.
pg_stat_statements.track_utilityonWhether to track utility commands (e.g. CREATE TABLE, VACUUM).
pg_stat_statements.track_planningoffWhether to track planning statistics (adds overhead).