Getting Started with Readyset on PostgreSQL
← Back to blogUncategorized

Getting Started with Readyset on PostgreSQL

PostgreSQL is one of the most potent relational databases, but performance can become a bottleneck as queries become complex. One of the most effective ways to reduce query latency without modifying application logic is caching, and Readyset provides a seamless solution. Readyset sits between your application and PostgreSQL, automatically caching queries and speeding up their response times considerably. In this guide, we’ll walk through: Installing Readyset on Ubuntu 24.04 using a .deb packa

Wagner Bianchi

Wagner Bianchi

2025-03-10 · 6 min read

PostgreSQL is one of the most potent relational databases, but performance can become a bottleneck as queries become complex. One of the most effective ways to reduce query latency without modifying application logic is caching, and Readyset provides a seamless solution.

Readyset sits between your application and PostgreSQL, automatically caching queries and speeding up their response times considerably.

In this guide, we’ll walk through:

Installing Readyset on Ubuntu 24.04 using a .deb package;

Configuring Readyset to connect to your upstream PostgreSQL database;

Running queries and caching results using the Northwind sample database;

Managing cached queries to optimize performance.

The Northwind PostgreSQL Sample Database can be downloaded and imported as below:

#: download the database dump and restore it
wget https://raw.githubusercontent.com/pthom/northwind_psql/master/northwind.sql
psql -U <dbuser> -W -h <db IP or DNS> -d template1 -c "create database northwind"
psql -U <dbuser> -W -h <db IP or DNS> -d northwind -f northwind.sql

You need to pay attention to security. We recommend creating a GROUP and a USER with the relevant configurations and permissions before you connect Readyset to your databases, which we call upstream databases. On the upstream database, you need to configure the wal_level as logical so Readyset can snapshot the database you have queries for caching and set up a logical replication to keep caching up-to-date.

Some other considerations for configuring your PostgreSQL with Readyset are at https://readyset.io/docs/reference/configure-your-database/postgres/generic-db-directions

Step 1: Prerequisites

Before setting up Readyset, ensure that a PostgreSQL database is already running. Readyset requires access to the database to snapshot the schema and cache queries.

Key Recommendations

Readyset should be installed on a different host than the PostgreSQL database to avoid resource contention.

The database server must allow Readyset to connect remotely.

For this tutorial, we assume:

  • PostgreSQL is running on 10.0.0.11:5432;
  • The Readyset is running on 10.0.0.12:5433;
  • A database named northwind is already restored;
  • A PostgreSQL user (readyset) with appropriate permissions exists.

Step 2: Installing Readyset on Ubuntu 24.04

2.1 Download the Readyset .deb Package

$ wget https://github.com/readysettech/readyset/releases/download/stable-250123/readyset_1.11.0-1_amd64.deb

2.2 Install Readyset Using dpk

$ dpkg -i readyset_1.11.0-1_amd64.deb

After installation, you should see:

Selecting previously unselected package readyset.
(Reading database ... 168043 files and directories currently installed.)
Preparing to unpack readyset_1.11.0-1_amd64.deb ...
Unpacking readyset (1.11.0-1) ...
Setting up readyset (1.11.0-1) ...
Created symlink /etc/systemd/system/multi-user.target.wants/readyset.service → /usr/lib/systemd/system/readyset.service.

You also need to install the psql on the Readyset host:

$ apt install -y postgresql-client-common postgresql-client

To make sure Readyset will be able to connect to the upstream database host, give it a try:

$ pg_isready -d northwind -h 10.0.0.11 -p 5432 -U readyset
10.0.0.11:5432 - accepting connections

Step 3: Configuring Readyset

Before starting Readyset, configure it in /etc/readyset/readyset.conf.

3.1 Open the Configuration File

vim /etc/readyset/readyset.conf

3.2 Set the Database Connection

UPSTREAM_DB_URL="postgres://readyset:<pass>@10.0.0.11:5432/northwind"
LISTEN_ADDRESS="0.0.0.0:5433"
  • UPSTREAM_DB_URL: PostgreSQL connection string for Readyset to snapshot the schema and proxy queries when needed.
  • LISTEN_ADDRESS: 5433 is the default Readyset port, ensuring compatibility when running on a separate host.

3.3 Start the Readyset Service

$ systemctl start readyset.service

To check if it’s running:

$ systemctl status readyset.service

If you want to check logs:

$ tail -n100 /var/lib/readyset/
…
2025-03-04T19:07:22.948022Z  INFO replicators::noria_adapter: Snapshot finished
2025-03-04T19:07:22.988330Z  INFO replicators::postgres_connector::connector: confirmed_flush_lsn="0/1B8DF80" wal_status="reserved"
2025-03-04T19:07:23.004533Z  INFO replicators::noria_adapter: Streaming replication started

Step 4: Connecting to Readyset

Once Readyset is running, connect using the PostgreSQL client:

psql -U readyset -h 10.0.12 -W -p 5433 -d northwind

For more convenience when running queries, I will disable the “pager less” for returning result sets and enable the timing for the Readyset psql PostgreSQL client.

$ echo "\pset pager off" >> ~/.psqlrc
$ echo "\timing" >> ~/.psqlrc

Since Readyset acts as a proxy, queries sent here will be ready to be cached.

Step 5: Running Your First Cached Query

Run a real-world query using PostgreSQL’s Northwind database:

northwind=> SELECT order_id
    , order_date
    , shipped_date
    , company_name
    , contact_name
FROM orders t1 JOIN customers t2 ON t1.customer_id = t2.customer_id
              JOIN employees t3 ON t1.employee_id = t3.employee_id
WHERE t1.ship_country='Brazil';
order_id | order_date | shipped_date |      company_name      |   contact_name   
----------+------------+--------------+------------------------+-------------------
   10250 | 1996-07-08 | 1996-07-12   | Hanari Carnes          | Mario Pontes
   10253 | 1996-07-10 | 1996-07-16   | Hanari Carnes          | Mario Pontes
...
   11059 | 1998-04-29 |              | Ricardo Adocicados     | Janete Limeira
   11068 | 1998-05-04 |              | Queen Cozinha          | Lúcia Carvalho
(83 rows)


Time: 54.390 ms

Since this is the first run, PostgreSQL processes it as usual. The time is ~54 ms.

We can use the EXPLAIN LAST STATEMENT Readyset command to see where this query was executed:

northwind=> EXPLAIN LAST STATEMENT;
 Query_destination | Readyset_error 
-------------------+----------------
 upstream          | ok
(1 row)

As expected, the query was proxied to the upstream database.

Step 6: Checking Proxied Queries

To check if Readyset intercepted the query:

SHOW PROXIED QUERIES;

Expected output:

northwind=> SHOW PROXIED QUERIES;
      query id      |                              proxied query                              | readyset supported | count 
--------------------+-------------------------------------------------------------------------+--------------------+-------
 q_33cfda0e3ac49afc | SELECT                                                                 +| yes                |     0
                    |   "order_id",                                                          +|                    | 
                    |   "order_date",                                                        +|                    | 
                    |   "shipped_date",                                                      +|                    | 
                    |   "company_name",                                                      +|                    | 
                    |   "contact_name"                                                       +|                    | 
                    | FROM                                                                   +|                    | 
                    |   "orders" AS "t1"                                                     +|                    | 
                    |   JOIN "customers" AS "t2" ON ("t1"."customer_id" = "t2"."customer_id")+|                    | 
                    |   JOIN "employees" AS "t3" ON ("t1"."employee_id" = "t3"."employee_id")+|                    | 
                    | WHERE                                                                  +|                    | 
                    |   ("t1"."ship_country" = $1)                                            |                    | 
Time: 0.858 ms

The query is “readyset supported”, as seen on the above output.

Step 7: Creating a Cache for the Query

To explicitly cache this query:

northwind=> CREATE CACHE ALWAYS FROM q_33cfda0e3ac49afc;
Time: 923.076 ms

Step 8: Verifying the Cache

Check if the query is cached:

northwind=> SHOW CACHES;
      query id      |     cache name     |                                query text                                | fallback behavior | count 
--------------------+--------------------+--------------------------------------------------------------------------+-------------------+-------
 q_33cfda0e3ac49afc | q_33cfda0e3ac49afc | SELECT                                                                  +| no fallback       | 0
                    |                    |   "public"."orders"."order_id",                                         +|                   | 
                    |                    |   "public"."orders"."order_date",                                       +|                   | 
                    |                    |   "public"."orders"."shipped_date",                                     +|                   | 
                    |                    |   "public"."customers"."company_name",                                  +|                   | 
                    |                    |   "public"."customers"."contact_name"                                   +|                   | 
                    |                    | FROM                                                                    +|                   | 
                    |                    |   "public"."orders"                                                     +|                   | 
                    |                    |   JOIN "public"."customers" ON (                                        +|                   | 
                    |                    |     "public"."orders"."customer_id" = "public"."customers"."customer_id"+|                   | 
                    |                    |   )                                                                     +|                   | 
                    |                    |   JOIN "public"."employees" ON (                                        +|                   | 
                    |                    |     "public"."orders"."employee_id" = "public"."employees"."employee_id"+|                   | 
                    |                    |   )                                                                     +|                   | 
                    |                    | WHERE                                                                   +|                   | 
                    |                    |   ("public"."orders"."ship_country" = $1)                                |                   | 
(1 row)
Time: 2.332 ms

At this point, Readyset is caching this query!

Step 9: Running the Cached Query

Rerun the query:

northwind=> SELECT order_id
    , order_date
    , shipped_date
    , company_name
    , contact_name
FROM orders t1 JOIN customers t2 ON t1.customer_id = t2.customer_id
              JOIN employees t3 ON t1.employee_id = t3.employee_id
WHERE t1.ship_country='Brazil';
 order_id | order_date | shipped_date |      company_name      |   contact_name    
----------+------------+--------------+------------------------+-------------------
    10250 | 1996-07-08 | 1996-07-12   | Hanari Carnes          | Mario Pontes
    10253 | 1996-07-10 | 1996-07-16   | Hanari Carnes          | Mario Pontes
…
    11059 | 1998-04-29 |              | Ricardo Adocicados     | Janete Limeira
    11068 | 1998-05-04 |              | Queen Cozinha          | Lúcia Carvalho
(83 rows)

Time: 1.232 ms

The same query execution, now using the Readyset cache took ~1 ms.

If we again EXPLAIN LAST STATEMENT:

northwind=> EXPLAIN LAST STATEMENT;
 Query_destination | Readyset_error 
-------------------+----------------
 readyset          | ok
(1 row)

🚀 The query execution time dropped more than 50 times! Readyset serves the result from the created cache, avoiding a full database load.

Step 10: Removing a Cached Query

To remove the cache, run:

northwind=> DROP CACHE q_33cfda0e3ac49afc;
DELETE 1
Time: 16.189 ms

Verify that it's gone:

SHOW CACHES;

Conclusion

With Readyset, you can significantly improve PostgreSQL query performance without modifying application code.

Using Readyset improves efficiency and performance orders of magnitude;

Queries are cached reducing upstream PostgreSQL load;

With this strategy you don't need to add more replicas or hardware;

Create custom caches for the queries you want fully optimized;

Using SHOW CACHES helps track performance improvements;

The EXPLAIN LAST STATEMENT shows you where the last statement was executed;

You don’t need any code changes, you can start with readyset now.

Connect with us!

Play with our Cloud Demo: https://readyset.cloud/

Our Slack: https://go.readyset.io/community

Our Github: https://github.com/readysettech/readyset

Want to see Readyset in action?

Book a demo and see how Readyset can accelerate your database.

Still scaling the hard way?

Modern applications demand instant performance, even under unpredictable load. Readyset helps you eliminate slow queries, stabilize latency, and scale confidently.

Revolutionize your database performance with Readyset

Serve requests at sub-millisecond latencies with the modern database scaling and query caching system for MySQL and PostgreSQL.

Join our newsletter

Stay updated with the latest news, insights, and developments from Readyset — straight to your inbox.

© 2026 Readyset. All rights reserved.