Skip to main content

Hyperdrive (Postgres and MySQL)

Cloudflare's product name is Hyperdrive (Postgres & MySQL). Official docs: developers.cloudflare.com/hyperdrive.

It is easy to read that title as "Cloudflare hosts our database." It does not.

What it is​

Hyperdrive sits between Cloudflare Workers and an origin database you already run (RDS, Aurora, Neon, PlanetScale, Cockroach, and other Postgres- or MySQL-compatible hosts).

It:

  1. Keeps a connection pool close to the origin database.
  2. Lets each Worker request open a cheap client to Hyperdrive at the edge (avoids TCP + TLS + DB auth round trips on every isolate).
  3. Optionally caches read queries.
  4. Hands your Worker a connection string (env.HYPERDRIVE.connectionString) that existing drivers (pg, mysql2, Prisma adapter) can use.

It does not:

  • Create or store your tables.
  • Replace PostgreSQL.
  • Accept connections from Lightsail, a laptop, or Prisma Studio.
  • Run Express, Socket.io, or ffmpeg.
  • Speak MySQL to a Postgres schema.

Pooling is transaction mode. A connection returns to the pool when the transaction ends. Do not hold long transactions to keep SET state. Create the driver inside the Worker handler, not as a global new Pool().

Limits (Cloudflare, subject to their current docs): origin pool about 20 connections on Free and 100 on Paid per Hyperdrive config; query duration cap 60s; cached response cap 50 MB. Workers can open many clients to Hyperdrive; Hyperdrive caps connections to RDS.

Verdict for RE360Muse​

main-backend is Express + Prisma 6 on a VM. That process should use DATABASE_URL → RDS (local: your Postgres). See Production setup.

ComputeDatabase path
Express on LightsailDirect DATABASE_URL to RDS
wrangler dev / WorkerHyperdrive binding → same RDS
Prisma migrate / seed / studioDirect DATABASE_URL to RDS (never Hyperdrive)

Do not set the API DATABASE_URL to a Hyperdrive string. Hyperdrive strings only exist inside a Worker binding.

MySQL support in Hyperdrive does not change our engine. Schema and Prisma datasource stay postgresql.

Use Hyperdrive later only if we add a Worker that must query the same Postgres (for example a globally distributed public listing read path). The Worker pools through Hyperdrive; Express still talks to RDS.

Origin database (still required)​

You need a reachable Postgres (or MySQL, if it were a different app).

For RE360Muse that origin is RDS PostgreSQL, same instance as the API.

RDS must allow Hyperdrive to connect:

If the instance is public, restrict the security group using Cloudflare's current Hyperdrive IP list: Firewall and networking. Do not leave 0.0.0.0/0 on 5432.

Prefer keeping RDS private and only allowing Lightsail via VPC peering. Add Hyperdrive later with VPC/Tunnel rather than opening the database to the internet.

SSL is required in production (sslmode=require). Hyperdrive can use custom CA material (TLS/SSL certificates).

Create a dedicated DB user for Hyperdrive if a Worker exists, with the least privilege that Worker needs (often read-only on public listing tables). Do not reuse the Express owner's password in the Worker if you can avoid it.

Worker setup (only if we add a Worker)​

Prerequisites: Cloudflare account, Node 20+, Wrangler, origin connection string (names only — paste values in the dashboard or CLI, not git).

npx wrangler login
npx wrangler hyperdrive create re360muse-rds --connection-string="postgres://USER:PASSWORD@RDS_HOST:5432/DBNAME"

Wrangler prints a Hyperdrive id. Bind it in wrangler.jsonc:

{
"name": "re360muse-listing-read",
"main": "src/index.ts",
"compatibility_date": "2026-09-18",
"hyperdrive": [
{
"binding": "HYPERDRIVE",
"id": "<HYPERDRIVE_ID>",
"localConnectionString": "postgres://USER:PASSWORD@127.0.0.1:5432/DBNAME"
}
]
}

localConnectionString is for wrangler dev. It talks to local Postgres, not to Cloudflare's pool.

Postgres driver​

npm i pg
npm i -D @types/pg

Create the client per request:

import { Client } from "pg";

export interface Env {
HYPERDRIVE: Hyperdrive;
}

export default {
async fetch(_req: Request, env: Env): Promise<Response> {
const client = new Client({
connectionString: env.HYPERDRIVE.connectionString,
});
await client.connect();
try {
const result = await client.query(
"select id, name from \"Property\" limit 20",
);
return Response.json(result.rows);
} finally {
await client.end();
}
},
};

Supported Postgres drivers (Cloudflare's list, not exhaustive): pg ≥ 8.13 (avoid 8.11.4), Postgres.js ≥ 3.4.4, Drizzle/Kysely on pg. See Connect to PostgreSQL.

Prisma from a Worker (not from Express)​

Express on Lightsail keeps the current Prisma Client and DATABASE_URL. A Worker that wants Prisma must use the driver adapter path Cloudflare documents (@prisma/adapter-pg, generate with --no-engine). That is a second runtime, not a change to main-backend.

import { PrismaPg } from "@prisma/adapter-pg";
import { PrismaClient } from "@prisma/client";

const adapter = new PrismaPg({
connectionString: env.HYPERDRIVE.connectionString,
});
const prisma = new PrismaClient({ adapter });

Migrations still run from CI or the API box against RDS directly:

npx prisma migrate deploy

MySQL (not used)​

Hyperdrive MySQL is mysql://USER:PASSWORD@HOST:3306/DB and mysql2 with disableEval: true on Workers. RE360Muse does not use this.

Pool size​

npx wrangler hyperdrive update <HYPERDRIVE_ID> --origin-connection-limit=10

Minimum 5. Stay well under RDS max_connections. Remember Express also opens Prisma connections to the same instance.

Credentials​

Rotate the origin password, then update the Hyperdrive config (rotating credentials). Updating Hyperdrive does not change Lightsail DATABASE_URL; update that env separately.

What we will not do​

  • Put Hyperdrive in front of main-backend.
  • Host the OS schema on D1, Hyperdrive-only, or MySQL.
  • Open RDS to the world "so Hyperdrive can try it."
  • Cache Paystack, wallet, or membership writes through Hyperdrive read cache.

Source of truth for production topology: Production setup.