diff --git a/README.md b/README.md
index e1cc204..5905fd0 100644
--- a/README.md
+++ b/README.md
@@ -57,16 +57,17 @@ The backend service will seed the database with fake data. It's worth noting tha
 
 The backend, frontend and workers have hot-reloading enabled, so changes made to the codebase should be reflected in the application on the next request.
 
-Once all containers have started, visit http://libre-wedding-planner.app.localhost/default/dashboard to load the application.
+Once all containers have started, visit:
+
+- http://libre-wedding-planner.app.localhost/default to load the application.
+- http://libre-wedding-planner.app.localhost/letter_opener/ to get a list of emails generated by the application.
+- http://pghero.libre-wedding-planner.app.localhost to load the [pghero](https://github.com/ankane/pghero) tool.
 
 ## Multitenancy
 
 LibreWeddingPlanner is designed to manage multiple weddings in a single host. All URLs (in the API and the frontend) are scoped under a slug that is unique per wedding. The slug is made of lowercase letters, numbers, and dashes (-).
 
 The development environment is seeded with a wedding whose slug is `default`.
-## Email delivery
-
-In the development environment, real emails will not be sent. You can visit http://libre-wedding-planner.app.localhost/letter_opener/ to get a list of emails generated by the application.
 
 ## Testing
 
diff --git a/db/migrate/20250122202836_enable_pg_stat_statements.rb b/db/migrate/20250122202836_enable_pg_stat_statements.rb
new file mode 100644
index 0000000..33dbf1a
--- /dev/null
+++ b/db/migrate/20250122202836_enable_pg_stat_statements.rb
@@ -0,0 +1,7 @@
+# Copyright (C) 2024 Manuel Bustillo
+
+class EnablePgStatStatements < ActiveRecord::Migration[8.0]
+  def change
+    enable_extension 'pg_stat_statements'
+  end
+end
diff --git a/db/migrate/20250122204932_create_pghero_stats_tables.rb b/db/migrate/20250122204932_create_pghero_stats_tables.rb
new file mode 100644
index 0000000..d813a38
--- /dev/null
+++ b/db/migrate/20250122204932_create_pghero_stats_tables.rb
@@ -0,0 +1,23 @@
+# Copyright (C) 2024 Manuel Bustillo
+
+class CreatePgheroStatsTables < ActiveRecord::Migration[8.0]
+  def up
+    execute <<~SQL
+      CREATE TABLE "pghero_query_stats" (
+        "id" bigserial primary key,
+        "database" text,
+        "user" text,
+        "query" text,
+        "query_hash" bigint,
+        "total_time" float,
+        "calls" bigint,
+        "captured_at" timestamp
+      );
+      CREATE INDEX ON "pghero_query_stats" ("database", "captured_at");
+    SQL
+  end
+
+  def down
+    drop_table :pghero_query_stats, if_exists: true, force: :cascade
+  end
+end
diff --git a/db/schema.rb b/db/schema.rb
index cfc1383..4de4cf6 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -1,7 +1,5 @@
 # Copyright (C) 2024 Manuel Bustillo
 
-# Copyright (C) 2024-2025 LibreWeddingPlanner contributors
-
 # This file is auto-generated from the current state of the database. Instead
 # of editing this file, please use the migrations feature of Active Record to
 # incrementally modify your database, and then regenerate this schema definition.
@@ -14,9 +12,10 @@
 #
 # It's strongly recommended that you check this file into your version control system.
 
-ActiveRecord::Schema[8.0].define(version: 2024_12_16_231415) do
+ActiveRecord::Schema[8.0].define(version: 2025_01_22_204932) do
   # These are extensions that must be enabled in order to support this database
   enable_extension "pg_catalog.plpgsql"
+  enable_extension "pg_stat_statements"
 
   # Custom types defined in this database.
   # Note that some types may not work with other database engines. Be careful if changing database.
@@ -71,6 +70,17 @@ ActiveRecord::Schema[8.0].define(version: 2024_12_16_231415) do
     t.index ["wedding_id"], name: "index_guests_on_wedding_id"
   end
 
+  create_table "pghero_query_stats", force: :cascade do |t|
+    t.text "database"
+    t.text "user"
+    t.text "query"
+    t.bigint "query_hash"
+    t.float "total_time"
+    t.bigint "calls"
+    t.datetime "captured_at", precision: nil
+    t.index ["database", "captured_at"], name: "pghero_query_stats_database_captured_at_idx"
+  end
+
   create_table "seats", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
     t.uuid "guest_id", null: false
     t.uuid "tables_arrangement_id", null: false
diff --git a/docker-compose.yml b/docker-compose.yml
index 93cff59..4c71d59 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -74,10 +74,19 @@ services:
       POSTGRES_USER: postgres
       POSTGRES_PASSWORD: postgres
       POSTGRES_DB: postgres
+    command: postgres -c shared_preload_libraries='pg_stat_statements' -c pg_stat_statements.track=all
     healthcheck:
       test: ['CMD-SHELL', 'pg_isready -U postgres']
       interval: 10s
       timeout: 5s
       retries: 5
-
+  pghero:
+    image: ankane/pghero
+    ports:
+      - 8080
+    environment:
+      DATABASE_URL: postgres://postgres:postgres@db:5432/postgres
+    depends_on:
+      db:
+        condition: service_healthy
    
\ No newline at end of file
diff --git a/nginx.conf b/nginx.conf
index 58c2bb9..2d4c4c2 100644
--- a/nginx.conf
+++ b/nginx.conf
@@ -23,3 +23,12 @@ server {
     }
 }
 
+server {
+    listen 80;
+    server_name pghero.libre-wedding-planner.app.localhost;
+
+    location / {
+        proxy_pass http://pghero:8080;
+        proxy_set_header Host $http_host;
+    }
+}
\ No newline at end of file