diff --git a/src/content/docs/hyperdrive/configuration/connect-to-private-database-vpc.mdx b/src/content/docs/hyperdrive/configuration/connect-to-private-database-vpc.mdx
new file mode 100644
index 000000000000000..bf37b42537b0310
--- /dev/null
+++ b/src/content/docs/hyperdrive/configuration/connect-to-private-database-vpc.mdx
@@ -0,0 +1,203 @@
+---
+pcx_content_type: how-to
+title: Connect to a private database using Workers VPC
+sidebar:
+ order: 5
+ badge:
+ text: Beta
+---
+
+import { Render, Tabs, TabItem, Steps } from "~/components";
+
+[Workers VPC](/workers-vpc/) provides a way to connect Hyperdrive to a private database without configuring Cloudflare Access applications or service tokens. Instead, you create a TCP [VPC Service](/workers-vpc/configuration/vpc-services/) that points to your database and pass its service ID to Hyperdrive.
+
+For the Tunnel and Access approach, refer to [Connect to a private database using Tunnel](/hyperdrive/configuration/connect-to-private-database/).
+
+
+
+## Prerequisites
+
+- A database in your private network, [configured to use TLS/SSL](/hyperdrive/examples/connect-to-postgres/#supported-tls-ssl-modes).
+- A [Cloudflare Tunnel](/workers-vpc/configuration/tunnel/) running in a network that can reach your database.
+- The **Connectivity Directory Admin** role on your Cloudflare account to create VPC Services.
+
+## 1. Set up a Cloudflare Tunnel
+
+If you do not already have a tunnel running in the same network as your database, create one.
+
+
+
+1. Go to the [Workers VPC dashboard](https://dash.cloudflare.com/?to=/:account/workers/vpc/tunnels) and select the **Tunnels** tab.
+
+2. Select **Create** to create a tunnel.
+
+3. Enter a name for your tunnel and select **Save tunnel**.
+
+4. Choose your operating system and architecture. The dashboard will provide installation instructions.
+
+5. Follow the provided commands to download, install, and run `cloudflared` with your unique token.
+
+
+
+The dashboard will confirm when the tunnel is connected. The tunnel must be able to reach your database host and port from within the private network.
+
+For full tunnel documentation, refer to [Cloudflare Tunnel for Workers VPC](/workers-vpc/configuration/tunnel/).
+
+## 2. Create a TCP VPC Service
+
+Create a VPC Service of type `tcp` that points to your database. Set the `--app-protocol` flag to `postgresql` or `mysql` so that Hyperdrive can optimize connections.
+
+
+
+
+```sh
+npx wrangler vpc service create my-postgres-db \
+ --type tcp \
+ --tcp-port 5432 \
+ --app-protocol postgresql \
+ --tunnel-id \
+ --ipv4
+```
+
+
+
+
+```sh
+npx wrangler vpc service create my-mysql-db \
+ --type tcp \
+ --tcp-port 3306 \
+ --app-protocol mysql \
+ --tunnel-id \
+ --ipv4
+```
+
+
+
+
+Replace:
+
+- `` with the tunnel ID from step 1.
+- `` with the private IP address of your database (for example, `10.0.0.5`). You can also use `--hostname` with a DNS name instead of `--ipv4`.
+
+The command will return a service ID. Save this value for the next step.
+
+You can also create a TCP VPC Service from the [Workers VPC dashboard](https://dash.cloudflare.com/?to=/:account/workers/vpc). Refer to [VPC Services](/workers-vpc/configuration/vpc-services/) for all configuration options.
+
+### TLS certificate verification
+
+Unlike Hyperdrive, which does not verify the origin server certificate by default, Workers VPC defaults to `verify_full` — it verifies both the certificate chain and the hostname. If your database uses a self-signed certificate or a certificate from a private certificate authority (CA), the TLS handshake will fail unless you adjust the verification mode.
+
+For databases with self-signed certificates, add `--cert-verification-mode` when creating the VPC Service:
+
+- `verify_ca` — Verifies the certificate chain but skips hostname verification. Use this when your database has a certificate signed by a CA you control but the hostname does not match the certificate.
+- `disabled` — Skips certificate verification entirely. Use this only for development or testing.
+
+For example, to create a VPC Service for a PostgreSQL database with a self-signed certificate:
+
+```sh
+npx wrangler vpc service create my-postgres-db \
+ --type tcp \
+ --tcp-port 5432 \
+ --app-protocol postgresql \
+ --tunnel-id \
+ --ipv4 \
+ --cert-verification-mode verify_ca
+```
+
+To update an existing VPC Service, use `wrangler vpc service update` with the same flag.
+
+:::note
+Workers VPC trusts publicly trusted certificates and [Cloudflare Origin CA certificates](/ssl/origin-configuration/origin-ca/). Uploading a custom CA certificate to Workers VPC is not supported yet. If your database uses a certificate signed by a private CA, set `--cert-verification-mode` to `verify_ca` or `disabled` until custom CA support is available.
+:::
+
+For the full list of verification modes, refer to [TLS certificate verification mode](/workers-vpc/configuration/vpc-services/#tls-certificate-verification-mode).
+
+## 3. Create a Hyperdrive configuration
+
+Use the `--service-id` flag to point Hyperdrive at the VPC Service you created. When you use `--service-id`, you do not provide `--origin-host`, `--origin-port`, or `--connection-string`. Hyperdrive routes traffic through the VPC Service instead.
+
+
+
+
+```sh
+npx wrangler hyperdrive create \
+ --service-id \
+ --database \
+ --user \
+ --password \
+ --scheme postgresql
+```
+
+
+
+
+```sh
+npx wrangler hyperdrive create \
+ --service-id \
+ --database \
+ --user \
+ --password \
+ --scheme mysql
+```
+
+
+
+
+Replace:
+
+- `` with the service ID from step 2.
+- `` with the name of your database.
+- `` and `` with your database credentials.
+
+If successful, the command will output a Hyperdrive configuration with an `id` field. Copy this ID for the next step.
+
+:::note
+The `--service-id` flag conflicts with `--origin-host`, `--origin-port`, `--connection-string`, `--access-client-id`, and `--access-client-secret`. You cannot combine these options. To update an existing Hyperdrive configuration to use a VPC Service, run `wrangler hyperdrive update` with the `--service-id` flag.
+:::
+
+## 4. Bind Hyperdrive to a Worker
+
+
+
+## 5. Query the database
+
+
+
+
+Use [node-postgres](https://node-postgres.com/) (`pg`) to send a test query.
+
+
+
+Deploy your Worker:
+
+```sh
+npx wrangler deploy
+```
+
+If you receive a list of `pg_tables` from your database when you access your deployed Worker, Hyperdrive is connected to your private database through Workers VPC.
+
+
+
+
+Use [mysql2](https://github.com/sidorares/node-mysql2) to send a test query.
+
+
+
+Deploy your Worker:
+
+```sh
+npx wrangler deploy
+```
+
+If you receive a list of tables from your database when you access your deployed Worker, Hyperdrive is connected to your private database through Workers VPC.
+
+
+
+
+## Next steps
+
+- Learn more about [how Hyperdrive works](/hyperdrive/concepts/how-hyperdrive-works/).
+- Configure [query caching](/hyperdrive/concepts/query-caching/) for Hyperdrive.
+- Review [VPC Service configuration options](/workers-vpc/configuration/vpc-services/) including TLS certificate verification.
+- Set up [high availability tunnels](/workers-vpc/configuration/tunnel/hardware-requirements/) for production workloads.
+- [Troubleshoot common issues](/hyperdrive/observability/troubleshooting/) when connecting a database to Hyperdrive.
diff --git a/src/content/docs/hyperdrive/configuration/connect-to-private-database.mdx b/src/content/docs/hyperdrive/configuration/connect-to-private-database.mdx
index 517674505f4e77a..8c79168e3c84444 100644
--- a/src/content/docs/hyperdrive/configuration/connect-to-private-database.mdx
+++ b/src/content/docs/hyperdrive/configuration/connect-to-private-database.mdx
@@ -11,6 +11,10 @@ import { TabItem, Tabs, Render, Steps, Details } from "~/components";
Hyperdrive can securely connect to your private databases using [Cloudflare Tunnel](/cloudflare-one/networks/connectors/cloudflare-tunnel/) and [Cloudflare Access](/cloudflare-one/access-controls/policies/).
+:::note
+You can also connect Hyperdrive to a private database using [Workers VPC](/hyperdrive/configuration/connect-to-private-database-vpc/), which does not require configuring Access applications or service tokens.
+:::
+
## How it works
When your database is isolated within a private network (such as a [virtual private cloud](https://www.cloudflare.com/learning/cloud/what-is-a-virtual-private-cloud) or an on-premise network), you must enable a secure connection from your network to Cloudflare.
diff --git a/src/content/docs/hyperdrive/get-started.mdx b/src/content/docs/hyperdrive/get-started.mdx
index 0788b033ff4f8a8..170773e82ee42ab 100644
--- a/src/content/docs/hyperdrive/get-started.mdx
+++ b/src/content/docs/hyperdrive/get-started.mdx
@@ -33,7 +33,7 @@ Before you begin, ensure you have completed the following:
1. Sign up for a [Cloudflare account](https://dash.cloudflare.com/sign-up/workers-and-pages) if you have not already.
2. Install [`Node.js`](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm). Use a Node version manager like [nvm](https://github.com/nvm-sh/nvm) or [Volta](https://volta.sh/) to avoid permission issues and change Node.js versions. [Wrangler](/workers/wrangler/install-and-update/) requires a Node version of `16.17.0` or later.
-3. Have a publicly accessible PostgreSQL or MySQL (or compatible) database. _If your database is in a private network (like a VPC)_, refer to [Connect to a private database](/hyperdrive/configuration/connect-to-private-database/) for instructions on using Cloudflare Tunnel with Hyperdrive.
+3. Have a publicly accessible PostgreSQL or MySQL (or compatible) database. _If your database is in a private network_, refer to [Connect to a private database using Workers VPC](/hyperdrive/configuration/connect-to-private-database-vpc/) or [Connect to a private database using Tunnel](/hyperdrive/configuration/connect-to-private-database/).
## 1. Log in
@@ -280,17 +280,16 @@ Populate your `index.ts` file with the following code:
```typescript
// mysql2 v3.13.0 or later is required
-import { createConnection } from 'mysql2/promise';
+import { createConnection } from "mysql2/promise";
export interface Env {
- // If you set another name in the Wrangler config file as the value for 'binding',
- // replace "HYPERDRIVE" with the variable name you defined.
- HYPERDRIVE: Hyperdrive;
+ // If you set another name in the Wrangler config file as the value for 'binding',
+ // replace "HYPERDRIVE" with the variable name you defined.
+ HYPERDRIVE: Hyperdrive;
}
export default {
- async fetch(request, env, ctx): Promise {
-
+ async fetch(request, env, ctx): Promise {
// Create a new connection on each request. Hyperdrive maintains the underlying
// database connection pool, so creating a new connection is fast.
const connection = await createConnection({
@@ -303,34 +302,29 @@ export default {
// The following line is needed for mysql2 compatibility with Workers
// mysql2 uses eval() to optimize result parsing for rows with > 100 columns
// Configure mysql2 to use static parsing instead of eval() parsing with disableEval
- disableEval: true
+ disableEval: true,
});
- try{
+ try {
// Sample query
- const [results, fields] = await connection.query(
- 'SHOW tables;'
- );
+ const [results, fields] = await connection.query("SHOW tables;");
// Return result rows as JSON
return new Response(JSON.stringify({ results, fields }), {
headers: {
- 'Content-Type': 'application/json',
- 'Access-Control-Allow-Origin': '*',
+ "Content-Type": "application/json",
+ "Access-Control-Allow-Origin": "*",
},
});
- }
- catch(e){
+ } catch (e) {
console.error(e);
return Response.json(
{ error: e instanceof Error ? e.message : e },
{ status: 500 },
);
}
-
- },
+ },
} satisfies ExportedHandler;
-
```
Upon receiving a request, the code above does the following:
diff --git a/src/content/docs/workers-vpc/configuration/vpc-services/index.mdx b/src/content/docs/workers-vpc/configuration/vpc-services/index.mdx
index 8633fee384d1461..51fba7861b6a17a 100644
--- a/src/content/docs/workers-vpc/configuration/vpc-services/index.mdx
+++ b/src/content/docs/workers-vpc/configuration/vpc-services/index.mdx
@@ -25,12 +25,18 @@ Workers VPC is currently in beta. Features and APIs may change before general av
A VPC Service consists of:
-- **Type**: Currently only `http` is supported (support for `tcp` coming soon)
+- **Type**: `http` for HTTP/HTTPS services, or `tcp` for TCP services (for example, PostgreSQL, MySQL)
- **Tunnel ID**: The Cloudflare Tunnel that provides network connectivity
- **Hostname or IPv4/IPv6 addresses**: The hostname, or IPv4 and/or IPv6 addresses to use to route to your service from the tunnel in your private network
-- **Ports**: HTTP and/or HTTPS port configuration (optional, defaults to 80/443)
+- **Ports**: For `http` type, HTTP and/or HTTPS port configuration (optional, defaults to 80/443). For `tcp` type, a TCP port (required).
+- **Application protocol** (TCP only): Optionally, specify `postgresql` or `mysql` to indicate the application-layer protocol for the TCP service
+- **TLS certificate verification mode**: Optionally, configure how the connection to the origin verifies TLS certificates
- **Resolver IPs**: Optionally, a specific resolver IP can be provided — when not provided, `cloudflared` will direct DNS traffic to the currently configured default system resolver.
+### HTTP services
+
+HTTP VPC Services allow Workers to make `fetch()` requests to private HTTP/HTTPS endpoints.
+
Requests are encrypted in flight until they reach your network via a tunnel, regardless of the scheme used in the URL provided to `fetch`. If the `http` scheme is used, a plaintext connection is established to the service from the tunnel.
The `https` scheme can be used for an encrypted connection within your network, between the tunnel and your service. When the `https` scheme is specified, a hostname provided to the `fetch()` operation is utilized as the Server Name Indication (SNI) value.
@@ -39,6 +45,14 @@ VPC Services default to allowing both `http` and `https` schemes to be used. You
When Workers VPC is unable to establish a connection to your service, `fetch()` will throw an exception.
+### TCP services
+
+TCP VPC Services allow connections to TCP-based services such as PostgreSQL and MySQL databases. Use the `tcp` service type with a `--tcp-port` to expose a TCP service.
+
+You can optionally specify an `--app-protocol` of `postgresql` or `mysql` to indicate the application-layer protocol. This metadata is used by other Cloudflare products, such as [Hyperdrive](/hyperdrive/), to locate TCP services using a supported wire protocol.
+
+TCP VPC Services are used with [Hyperdrive](/hyperdrive/) to connect Workers to private databases. Refer to [Connect to a private database using Workers VPC](/hyperdrive/configuration/connect-to-private-database-vpc/) for a complete guide.
+
:::note
The [VPC Service configuration](/workers-vpc/configuration/vpc-services/#vpc-service-configuration) host and port(s) will always be used to connect and route requests to your services, even if a different host or port is present in the URL provided to the `fetch()` operation in the Worker code.
@@ -58,9 +72,23 @@ When using the `https` scheme, the tunnel verifies the TLS certificate presented
If your origin service presents a certificate that is not issued by a publicly trusted CA or by Cloudflare Origin CA, the TLS handshake will fail and `fetch()` will throw an exception.
-## Configuration example
+### TLS certificate verification mode
+
+You can configure how the connection to your origin service verifies TLS certificates by setting the `--cert-verification-mode` option when creating or updating a VPC Service. This applies to both HTTP and TCP service types.
+
+| Mode | Description |
+| ------------- | ------------------------------------------------------------- |
+| `verify_full` | Verify the full certificate chain and hostname (default) |
+| `verify_ca` | Verify the certificate chain only, skip hostname verification |
+| `disabled` | Do not verify the server certificate |
+
+## Configuration examples
+
+These configurations represent the expected contract of the [REST API for creating a VPC Service](/api/resources/connectivity/subresources/directory/subresources/services/), a type of service within the broader connectivity directory.
-The following is an example of a VPC Service for a service using custom HTTP and HTTPS ports, and both IPv4 and IPv6 addresses. These configurations represent the expected contract of the [REST API for creating a VPC Service](/api/resources/connectivity/subresources/directory/subresources/services/), a type of service within the broader connectivity directory.
+### HTTP service with IP addresses
+
+The following is an example of an HTTP VPC Service using custom HTTP and HTTPS ports, and both IPv4 and IPv6 addresses.
```jsonc
{
@@ -82,7 +110,9 @@ The following is an example of a VPC Service for a service using custom HTTP and
}
```
-The following is an example of a VPC Service for a service using custom HTTP and HTTPS ports as well, using a hostname. Note that since we are using a hostname, we must provide our service with a `resolver_network` that optionally has `resolver_ips`.
+### HTTP service with hostname
+
+The following is an example of an HTTP VPC Service using a hostname. When using a hostname, provide a `resolver_network` that optionally includes `resolver_ips`.
```jsonc
{
@@ -104,6 +134,50 @@ The following is an example of a VPC Service for a service using custom HTTP and
}
```
+### TCP service (for example, PostgreSQL)
+
+The following is an example of a TCP VPC Service for a PostgreSQL database.
+
+```jsonc
+{
+ "type": "tcp",
+ "name": "my-postgres-db",
+ "tcp_port": 5432,
+ "app_protocol": "postgresql", // Optional: "postgresql" or "mysql"
+
+ "host": {
+ "ipv4": "10.0.0.5",
+ "network": {
+ "tunnel_id": "0191dce4-9ab4-7fce-b660-8e5dec5172da",
+ },
+ },
+}
+```
+
+### Service with TLS certificate verification
+
+The following example creates a TCP service with `verify_ca` certificate verification mode.
+
+```jsonc
+{
+ "type": "tcp",
+ "name": "my-postgres-db",
+ "tcp_port": 5432,
+ "app_protocol": "postgresql",
+
+ "host": {
+ "ipv4": "10.0.0.5",
+ "network": {
+ "tunnel_id": "0191dce4-9ab4-7fce-b660-8e5dec5172da",
+ },
+ },
+
+ "tls_settings": {
+ "cert_verification_mode": "verify_ca",
+ },
+}
+```
+
## Workers binding configuration
Once you have created a VPC Service, you can bind it to your Worker:
diff --git a/src/content/docs/workers-vpc/configuration/vpc-services/terraform.mdx b/src/content/docs/workers-vpc/configuration/vpc-services/terraform.mdx
index e99014102681962..43183707c4faf93 100644
--- a/src/content/docs/workers-vpc/configuration/vpc-services/terraform.mdx
+++ b/src/content/docs/workers-vpc/configuration/vpc-services/terraform.mdx
@@ -20,7 +20,7 @@ The `cloudflare_connectivity_directory_service` resource creates a VPC Service i
### Hostname-based configuration
-When using a hostname, provide `host.hostname` with a `resolver_network` block. This parallels the hostname-based [JSON configuration example](/workers-vpc/configuration/vpc-services/#configuration-example).
+When using a hostname, provide `host.hostname` with a `resolver_network` block. This parallels the hostname-based [JSON configuration example](/workers-vpc/configuration/vpc-services/#configuration-examples).
```tf
resource "cloudflare_connectivity_directory_service" "my_private_api" {
@@ -59,7 +59,7 @@ resource "cloudflare_connectivity_directory_service" "my_private_api" {
### IP-based configuration
-When using IP addresses, provide `host.ipv4` and/or `host.ipv6` with a `network` block. This parallels the IP-based [JSON configuration example](/workers-vpc/configuration/vpc-services/#configuration-example).
+When using IP addresses, provide `host.ipv4` and/or `host.ipv6` with a `network` block. This parallels the IP-based [JSON configuration example](/workers-vpc/configuration/vpc-services/#configuration-examples).
```tf
resource "cloudflare_connectivity_directory_service" "my_private_api" {
@@ -79,9 +79,59 @@ resource "cloudflare_connectivity_directory_service" "my_private_api" {
}
```
+### TCP service configuration
+
+For TCP services (for example, databases), set `type = "tcp"` and provide a `tcp_port`. You can optionally specify an `app_protocol` of `postgresql` or `mysql`.
+
+```tf
+resource "cloudflare_connectivity_directory_service" "my_database" {
+ account_id = var.account_id
+ name = "my-postgres-db"
+ type = "tcp"
+ tcp_port = 5432
+ app_protocol = "postgresql"
+
+ host = {
+ ipv4 = "10.0.0.5"
+ network = {
+ tunnel_id = var.tunnel_id
+ }
+ }
+}
+```
+
+### TLS certificate verification
+
+To configure the TLS certificate verification mode for the connection to the origin, add a `tls_settings` block:
+
+```tf
+resource "cloudflare_connectivity_directory_service" "my_database" {
+ account_id = var.account_id
+ name = "my-postgres-db"
+ type = "tcp"
+ tcp_port = 5432
+ app_protocol = "postgresql"
+
+ host = {
+ ipv4 = "10.0.0.5"
+ network = {
+ tunnel_id = var.tunnel_id
+ }
+ }
+
+ tls_settings = {
+ cert_verification_mode = "verify_ca"
+ }
+}
+```
+
+Valid values for `cert_verification_mode` are `verify_full` (default), `verify_ca`, and `disabled`. Refer to [TLS certificate verification mode](/workers-vpc/configuration/vpc-services/#tls-certificate-verification-mode) for details.
+
### Port configuration
-Ports are optional and default to 80 (HTTP) and 443 (HTTPS). To enforce a single scheme, provide only one of `http_port` or `https_port`. Refer to [VPC Service configuration](/workers-vpc/configuration/vpc-services/#vpc-service-configuration) for how scheme enforcement and port behavior work.
+For HTTP services, ports are optional and default to 80 (HTTP) and 443 (HTTPS). To enforce a single scheme, provide only one of `http_port` or `https_port`. Refer to [VPC Service configuration](/workers-vpc/configuration/vpc-services/#vpc-service-configuration) for how scheme enforcement and port behavior work.
+
+For TCP services, `tcp_port` is required.
## Workers binding configuration
@@ -151,6 +201,11 @@ data "cloudflare_connectivity_directory_services" "all_http" {
account_id = var.account_id
type = "http"
}
+
+data "cloudflare_connectivity_directory_services" "all_tcp" {
+ account_id = var.account_id
+ type = "tcp"
+}
```
## Resource schema reference
@@ -160,11 +215,15 @@ resource "cloudflare_connectivity_directory_service" "example" {
# Required
account_id = "your-account-id" # Account identifier
name = "my-private-api" # Human-readable name
- type = "http" # Service type (only "http" supported)
+ type = "http" # Service type: "http" or "tcp"
- # Optional
- http_port = 80 # HTTP port (default: 80)
- https_port = 443 # HTTPS port (default: 443)
+ # HTTP-specific (optional, defaults to 80/443)
+ http_port = 80 # HTTP port
+ https_port = 443 # HTTPS port
+
+ # TCP-specific (tcp_port is required when type = "tcp")
+ # tcp_port = 5432 # TCP port
+ # app_protocol = "postgresql" # Optional: "postgresql" or "mysql"
host = {
# Use hostname OR ipv4/ipv6, not both
@@ -184,6 +243,11 @@ resource "cloudflare_connectivity_directory_service" "example" {
# }
}
+ # Optional TLS settings
+ # tls_settings = {
+ # cert_verification_mode = "verify_full" # "verify_full", "verify_ca", or "disabled"
+ # }
+
# Read-only (computed by the API)
# id — Terraform resource ID
# service_id — VPC Service ID (use this for Worker bindings)
diff --git a/src/content/docs/workers-vpc/examples/private-database.mdx b/src/content/docs/workers-vpc/examples/private-database.mdx
new file mode 100644
index 000000000000000..d348d9c3930b40a
--- /dev/null
+++ b/src/content/docs/workers-vpc/examples/private-database.mdx
@@ -0,0 +1,133 @@
+---
+title: Connect to a private database
+pcx_content_type: example
+---
+
+import { WranglerConfig } from "~/components";
+
+This example demonstrates how to query a private PostgreSQL database from a Worker using [Workers VPC](/workers-vpc/) and [Hyperdrive](/hyperdrive/). The Worker connects to a database that is not exposed to the public Internet, with Hyperdrive providing connection pooling and query acceleration.
+
+## Prerequisites
+
+- A PostgreSQL database running in your private network (for example, on port 5432)
+- A [Cloudflare Tunnel](/workers-vpc/configuration/tunnel/) connected to the private network where your database runs
+- A Cloudflare account with Workers VPC access
+
+## 1. Create a TCP VPC Service
+
+Create a [VPC Service](/workers-vpc/configuration/vpc-services/) of type `tcp` that points to your PostgreSQL database. The `--app-protocol postgresql` flag allows Hyperdrive to optimize connections.
+
+```sh
+npx wrangler vpc service create my-postgres-db \
+ --type tcp \
+ --tcp-port 5432 \
+ --app-protocol postgresql \
+ --tunnel-id \
+ --ipv4
+```
+
+Replace `` with the ID of the tunnel connected to your database network. Replace `` with the private IP address of your database (for example, `10.0.1.50`).
+
+Note the service ID returned for the next step.
+
+## 2. Create a Hyperdrive configuration
+
+Use `wrangler hyperdrive create` with the `--service-id` flag to route traffic through the VPC Service. When you use `--service-id`, you do not provide `--origin-host`, `--origin-port`, or `--connection-string`.
+
+```sh
+npx wrangler hyperdrive create my-private-db \
+ --service-id \
+ --database \
+ --user \
+ --password \
+ --scheme postgresql
+```
+
+If successful, the command will output a Hyperdrive configuration with an `id` field. Copy this ID for the next step.
+
+## 3. Configure your Worker
+
+Update your Wrangler configuration file with the Hyperdrive binding. Replace `` with the ID from the previous step.
+
+
+```jsonc
+{
+ "$schema": "./node_modules/wrangler/config-schema.json",
+ "name": "private-db-worker",
+ "main": "src/index.ts",
+ "compatibility_date": "$today",
+ "compatibility_flags": ["nodejs_compat"],
+ "hyperdrive": [
+ {
+ "binding": "HYPERDRIVE",
+ "id": ""
+ }
+ ]
+}
+```
+
+
+The `nodejs_compat` compatibility flag is required for the `pg` database driver.
+
+## 4. Implement the Worker
+
+Install the [node-postgres](https://node-postgres.com/) driver:
+
+```sh
+npm install pg
+```
+
+Create a Worker that connects to your database through Hyperdrive:
+
+```ts title="src/index.ts"
+import { Client } from "pg";
+
+export interface Env {
+ HYPERDRIVE: Hyperdrive;
+}
+
+export default {
+ async fetch(request, env, ctx): Promise {
+ const client = new Client({
+ connectionString: env.HYPERDRIVE.connectionString,
+ });
+
+ try {
+ await client.connect();
+ const result = await client.query("SELECT * FROM pg_tables LIMIT 10");
+ return Response.json(result.rows);
+ } catch (e) {
+ console.error(e);
+ return Response.json(
+ { error: e instanceof Error ? e.message : e },
+ { status: 500 },
+ );
+ }
+ },
+} satisfies ExportedHandler;
+```
+
+The Worker creates a new `Client` on each request using the Hyperdrive connection string. Hyperdrive maintains the underlying connection pool, so creating a new client is fast.
+
+## 5. Deploy and test
+
+Deploy your Worker:
+
+```sh
+npx wrangler deploy
+```
+
+Send a request to verify the connection:
+
+```sh
+curl https://private-db-worker..workers.dev
+```
+
+A successful response returns a JSON array of rows from your database.
+
+## Next steps
+
+- Learn more about [how Hyperdrive works](/hyperdrive/concepts/how-hyperdrive-works/)
+- Configure [query caching](/hyperdrive/concepts/query-caching/) for Hyperdrive
+- Review [VPC Service configuration options](/workers-vpc/configuration/vpc-services/) including TLS certificate verification
+- Explore [other examples](/workers-vpc/examples/)
diff --git a/src/content/docs/workers-vpc/get-started.mdx b/src/content/docs/workers-vpc/get-started.mdx
index a5748e2dc78a7af..bb5b423b66a9be9 100644
--- a/src/content/docs/workers-vpc/get-started.mdx
+++ b/src/content/docs/workers-vpc/get-started.mdx
@@ -128,6 +128,8 @@ The dashboard will display your new VPC Service with a unique Service ID. Save t
+For an HTTP service:
+
```sh
npx wrangler vpc service create my-private-api \
--type http \
@@ -135,15 +137,28 @@ npx wrangler vpc service create my-private-api \
--hostname
```
+For a TCP service (for example, a PostgreSQL database):
+
+```sh
+npx wrangler vpc service create my-database \
+ --type tcp \
+ --tcp-port 5432 \
+ --app-protocol postgresql \
+ --tunnel-id \
+ --ipv4
+```
+
Replace:
- `` with your tunnel ID from step 2
- `` with your internal service hostname (for example, `internal-api.company.local`)
+- `` with the private IP address of your service (for example, `10.0.1.50`)
You can also:
-- Create services using IP addresses by replacing `--hostname ` with `--ipv4 ` (for example, `--ipv4 10.0.1.50`), `--ipv6 ` (for example, `--ipv6 fe80::1`), or both for dual-stack configuration (`--ipv4 10.0.1.50 --ipv6 fe80::1`)
-- Specify custom ports by adding `--http-port ` and/or `--https-port ` (for example, `--http-port 8080 --https-port 8443`)
+- Create services using IP addresses by replacing `--hostname ` with `--ipv4 `, `--ipv6 `, or both for dual-stack configuration
+- Specify custom ports for HTTP services by adding `--http-port ` and/or `--https-port ` (for example, `--http-port 8080 --https-port 8443`)
+- Set the TLS certificate verification mode with `--cert-verification-mode` (`verify_full`, `verify_ca`, or `disabled`)
The command will return a service ID. Save this for the next step.
@@ -188,7 +203,9 @@ export default {
// This is a simple proxy scenario.
// In this case, you will need to replace the URL with the proper protocol (http vs. https), hostname and port of the service.
// For example, this could be "http://localhost:1111", "http://192.0.0.1:3000", "https://my-internal-api.example.com"
- const targetUrl = new URL(`http://:${url.pathname}${url.search}`);
+ const targetUrl = new URL(
+ `http://:${url.pathname}${url.search}`,
+ );
// Create new request with the target URL but preserve all other properties
const proxyRequest = new Request(targetUrl, {
@@ -202,7 +219,6 @@ export default {
return response;
},
} satisfies ExportedHandler;
-
```
## 6. Test locally
diff --git a/src/content/docs/workers-vpc/index.mdx b/src/content/docs/workers-vpc/index.mdx
index acbf66b75c83fb3..f25314b18613867 100644
--- a/src/content/docs/workers-vpc/index.mdx
+++ b/src/content/docs/workers-vpc/index.mdx
@@ -27,9 +27,9 @@ Securely connect your private cloud to Cloudflare to build cross-cloud apps.
-Workers VPC allows you to connect your Workers to your private APIs and services in external clouds (AWS, Azure, GCP, on-premise, etc.) that are not accessible from the public Internet.
+Workers VPC allows you to connect your Workers to your private APIs, services, and databases in external clouds (AWS, Azure, GCP, on-premise, and others) that are not accessible from the public Internet.
-With Workers VPC, you can configure a [Cloudflare Tunnel](/cloudflare-one/networks/connectors/cloudflare-tunnel/) to establish secure, private connections from your private networks to Cloudflare. Then, you can configure a [VPC Service](/workers-vpc/configuration/vpc-services/) for each service in the external private network you need to connect to, and use [VPC Service bindings](/workers-vpc/api/) to connect from Workers.
+With Workers VPC, you can configure a [Cloudflare Tunnel](/cloudflare-one/networks/connectors/cloudflare-tunnel/) to establish secure, private connections from your private networks to Cloudflare. Then, you can configure a [VPC Service](/workers-vpc/configuration/vpc-services/) for each service in the external private network you need to connect to, and use [VPC Service bindings](/workers-vpc/api/) to connect from Workers. VPC Services support both HTTP and TCP service types, allowing you to connect to web APIs and databases (through [Hyperdrive](/hyperdrive/)).
:::note