Prisma + Neon PostgreSQL Connection Timeout: Debugging Node.js IPv4/IPv6 Issues on Windows
Tags: Node.js, Prisma, PostgreSQL, Neon, Networking, Debugging I recently encountered a frustrating database connectivity issue while working on a Next.js application using Prisma 7.8.0 and Neon PostgreSQL. My database queries were failing with connection timeouts. Initially, I suspected Prisma, the Neon serverless adapter, or even the database itself. But after investigating the problem layer by layer, I discovered something more interesting: the issue was related to how Node.js handled network connections in an environment where IPv4 was working but IPv6 connectivity was unavailable. In this article, I'll explain how I investigated the problem, the tests I performed, what the evidence revealed, and the configuration that eventually fixed it.

1. The Technology Stack
My development environment consisted of:
| Technology | Version |
|---|---|
| OS | Windows 11 |
| Node.js | 22.22.2 |
| Next.js | 16.2.0 |
| Prisma | 7.8.0 |
| Database | Neon PostgreSQL |
| Neon adapter | @prisma/adapter-neon |
| PostgreSQL adapter | @prisma/adapter-pg |
| PostgreSQL driver | pg |
The Neon database was hosted in the AWS Asia Pacific (Sydney) region.
I was using the pooled Neon connection string for application queries.
2. The Initial Problem
I initially encountered an error while executing a Prisma query:
const subscriptions = await prisma.subscription.findMany({
where: {
firebase_uid,
},
});
The query was relatively simple, but it failed with a WebSocket connection error.
The error contained details similar to:
ErrorEvent {
clientVersion: '7.8.0',
[Symbol(kTarget)]: WebSocket {
_closeCode: 1006,
_readyState: 3,
_url: 'wss://<neon-hostname>/v2'
},
[Symbol(kError)]: AggregateError: {
code: 'ETIMEDOUT'
}
}
I first suspected that something was wrong with the query or Prisma configuration.
However, simplifying the query didn't help.
That was my first indication that the problem was probably happening before query execution.
3. First Investigation: Is Neon Reachable?
Before changing my application code, I wanted to establish whether my machine could reach the Neon infrastructure.
I started with Windows PowerShell:
Test-NetConnection <neon-hostname> -Port 443
Then:
Test-NetConnection <neon-hostname> -Port 5432
Both tests reported successful TCP connectivity.
Next, I tested HTTPS using curl:
curl.exe -v --connect-timeout 10 https://<neon-hostname>/
The endpoint returned:
HTTP/1.1 400 Bad Request
"query is not supported"
This response was expected because I was requesting the root endpoint using GET.
The important part was that the server responded.
Conclusion: My Windows environment could reach the Neon endpoint over HTTPS, and TCP connectivity tests also succeeded.
But this didn't necessarily mean Node.js could establish the same connections.
4. Second Investigation: Does Node.js Have a Connectivity Problem?
I tested Node.js HTTPS connectivity to a normal website:
node -e "fetch('https://example.com').then(r=>console.log(r.status)).catch(console.error)"
Result:
200
Then I tested the Neon endpoint:
node -e "fetch('https://<neon-hostname>/').then(r=>console.log(r.status)).catch(console.error)"
Initially, it failed with:
ETIMEDOUT
This was interesting.
Windows curl could reach Neon, but Node.js fetch was timing out.
I then tried changing Node.js DNS address ordering.
In Windows CMD:
set NODE_OPTIONS=--dns-result-order=ipv4first
After that, I tested the Neon endpoint again.
It returned HTTP 400 instead of timing out.
This was an important clue.
Node.js was now able to reach the Neon endpoint after prioritizing IPv4 addresses.
However, my original Prisma connection was still failing.
5. Third Investigation: Is the Problem Specific to Prisma's Neon Adapter?
I wanted to eliminate Next.js and Prisma from the investigation.
So I created a standalone WebSocket test using the ws package.
The test explicitly forced IPv4 DNS resolution:
import WebSocket from "ws";
import dns from "node:dns";
const hostname = "<neon-hostname>";
const ws = new WebSocket(`wss://${hostname}/v2`, {
handshakeTimeout: 15000,
lookup: (hostname, options, callback) => {
dns.lookup(
hostname,
{
family: 4,
all: options?.all ?? false,
},
callback
);
},
});
ws.on("open", () => {
console.log("WebSocket connected successfully!");
ws.close();
});
ws.on("error", (error) => {
console.error("WebSocket connection failed:", error);
});
ws.on("close", (code, reason) => {
console.log("WebSocket closed:", code, reason.toString());
});
Result:
WebSocket connected successfully!
WebSocket closed: { code: 1006, reason: '' }
The connection was established successfully.
The 1006 close code occurred because the test closed the connection without a normal WebSocket closing handshake.
This demonstrated that a Node.js WebSocket connection to Neon could succeed when IPv4 resolution was explicitly enforced.
However, the Prisma Neon adapter continued to fail.
I also tested Prisma independently, outside Next.js, and reproduced the same timeout.
At this point, I knew the problem was not caused by my API route, server action, or React components.
6. Fourth Investigation: Try the Standard PostgreSQL Adapter
Prisma supports multiple database adapters.
Instead of using the Neon serverless adapter, I decided to test the standard PostgreSQL adapter.
I installed:
npm install @prisma/adapter-pg@7.8.0 pg
Then created a standalone test:
import "dotenv/config";
import { PrismaPg } from "@prisma/adapter-pg";
import { PrismaClient } from "@prisma/client";
const adapter = new PrismaPg({
connectionString: process.env.DATABASE_URL!,
});
const prisma = new PrismaClient({ adapter });
async function main() {
try {
console.log("Testing Prisma PostgreSQL adapter...");
const result = await prisma.$queryRaw`
SELECT 1 AS connected
`;
console.log("Connection successful:", result);
} catch (error) {
console.error("Connection failed:");
console.dir(error, { depth: 10 });
} finally {
await prisma.$disconnect();
}
}
main();
Unfortunately, this also failed:
PrismaClientKnownRequestError
code: ETIMEDOUT
Even enabling IPv4-first ordering did not resolve the problem.
This was a significant finding.
The problem was not limited to Prisma's Neon WebSocket adapter.
7. Fifth Investigation: Examine DNS Resolution
I decided to investigate DNS more closely.
First, I ran:
nslookup <neon-hostname>
The result was:
Server: UnKnown
Address: 2401:4900:50:9::7a9
No response from server
My configured DNS server was not responding to the query.
I also tested Node.js DNS resolution:
import dns from "node:dns/promises";
const hostname = "<neon-hostname>";
const addresses = await dns.resolve4(hostname);
console.log(addresses);
This failed with:
ECONNREFUSED
But there was something interesting.
When I used Node.js's operating-system-based DNS lookup:
import dns from "node:dns";
dns.lookup(
"<neon-hostname>",
{ all: true },
(error, addresses) => {
console.log({ error, addresses });
}
);
It successfully returned multiple addresses.
IPv4:
54.153.234.207
54.206.85.193
3.106.69.51
IPv6:
2406:da1c:b7c:d0e:9858:9714:c422:b17b
2406:da1c:b7c:d20:2c57:4c4d:e935:9480
2406:da1c:b7c:d11:b952:e163:87c0:5972
This distinction was important.
dns.resolve4() and dns.lookup() do not use the same resolution mechanism on Node.js.
The former performs DNS resolution directly, while the latter normally uses the operating system's name-resolution facilities.
Therefore, my DNS environment had a problem, but hostname resolution was not completely unavailable.
8. Sixth Investigation: Test IPv4 and IPv6 Separately
Now I wanted to determine whether the returned IP addresses were actually reachable.
I wrote a small TCP diagnostic script:
import dns from "node:dns";
import net from "node:net";
const hostname = "<neon-hostname>";
dns.lookup(hostname, { all: true }, (error, addresses) => {
if (error) {
console.error(error);
return;
}
console.log("Resolved addresses:", addresses);
for (const address of addresses) {
const socket = net.createConnection({
host: address.address,
port: 5432,
family: address.family,
timeout: 10000,
});
socket.on("connect", () => {
console.log(`TCP PASS: ${address.address}:5432`);
socket.destroy();
});
socket.on("timeout", () => {
console.log(`TCP TIMEOUT: ${address.address}:5432`);
socket.destroy();
});
socket.on("error", (error) => {
console.log(
`TCP FAIL: ${address.address}:5432`,
error.code
);
});
}
});
The results were revealing.
IPv6 connections failed with:
ENETUNREACH
IPv4 connections succeeded:
TCP PASS: 3.106.69.51:5432
TCP PASS: 54.153.234.207:5432
TCP PASS: 54.206.85.193:5432
This established that my environment could reach Neon over IPv4 but did not have usable IPv6 connectivity.
However, I still needed to establish whether this was responsible for the PostgreSQL query failures.
9. Seventh Investigation: Connect Directly to PostgreSQL Using IPv4
I created a direct PostgreSQL connection using the pg package.
For this diagnostic test, I supplied a fixed IPv4 address while preserving the original hostname for TLS certificate verification.
import "dotenv/config";
import pg from "pg";
import { parse } from "pg-connection-string";
const { Client } = pg;
const hostname = "<neon-hostname>";
const config = parse(process.env.DATABASE_URL);
config.host = "54.153.234.207";
config.port = 5432;
config.ssl = {
rejectUnauthorized: true,
servername: hostname,
};
const client = new Client(config);
try {
console.log("Connecting to Neon PostgreSQL...");
await client.connect();
console.log("PostgreSQL connection established!");
const result = await client.query(
"SELECT NOW() AS time"
);
console.log("Query successful:", result.rows);
} catch (error) {
console.error("Connection failed:", error);
} finally {
await client.end().catch(() => {});
}
Result:
PostgreSQL connection established!
Query successful: [{ time: ... }]
This was a major breakthrough.
The database was healthy.
My credentials were valid.
TLS verification worked.
The database could execute SQL successfully.
The problem appeared to be in the hostname-based connection path rather than PostgreSQL itself.
10. Eighth Investigation: Does Prisma Work With a Fixed IPv4 Address?
I wanted to verify that the Prisma PostgreSQL adapter itself was working.
So I repeated the same fixed-IP test using Prisma.
import "dotenv/config";
import { parse } from "pg-connection-string";
import { PrismaPg } from "@prisma/adapter-pg";
import { PrismaClient } from "@prisma/client";
const hostname = "<neon-hostname>";
const config = parse(process.env.DATABASE_URL!);
config.host = "54.153.234.207";
config.port = 5432;
config.ssl = {
rejectUnauthorized: true,
servername: hostname,
};
const adapter = new PrismaPg(config);
const prisma = new PrismaClient({ adapter });
async function main() {
try {
console.log("Testing Prisma with fixed IPv4...");
const result = await prisma.$queryRaw`
SELECT NOW() AS time, 1 AS connected
`;
console.log("Prisma connection successful:", result);
} catch (error) {
console.error("Prisma connection failed:", error);
} finally {
await prisma.$disconnect();
}
}
main();
Result:
Testing Prisma with fixed IPv4...
Prisma connection successful:
[
{
time: ...,
connected: 1
}
]
This was the decisive comparison.
Prisma could connect and execute SQL against Neon when the connection used a fixed IPv4 address.
I also tested all three IPv4 addresses independently. All three successfully executed PostgreSQL queries.
Therefore, the issue was not caused by one unhealthy Neon endpoint.
11. The Actual Fix
After isolating the problem, I tested two Node.js networking configurations.
import dns from "node:dns";
import net from "node:net";
dns.setDefaultResultOrder("ipv4first");
net.setDefaultAutoSelectFamily(false);
And surprisingly, everything started working.
My original hostname-based PostgreSQL connection worked again, and the application could successfully execute Prisma queries.
What does this configuration do?
dns.setDefaultResultOrder("ipv4first")
Node.js can receive multiple IP addresses when resolving a hostname.
This setting prioritizes IPv4 addresses.
Since my network had working IPv4 connectivity but unavailable IPv6 connectivity, preferring IPv4 helped avoid the problematic IPv6 path.
net.setDefaultAutoSelectFamily(false)
Node.js supports automatic address-family selection when connecting to hosts that resolve to multiple addresses.
This setting disables that automatic selection behavior for new connections.
In my environment, disabling it alongside IPv4-first ordering resolved the connection issue.
Final database initialization
My database initialization now follows this structure:
import dns from "node:dns";
import net from "node:net";
import { PrismaPg } from "@prisma/adapter-pg";
import { PrismaClient } from "@prisma/client";
// Configure networking before creating database connections.
dns.setDefaultResultOrder("ipv4first");
net.setDefaultAutoSelectFamily(false);
const adapter = new PrismaPg({
connectionString: process.env.DATABASE_URL!,
});
export const prisma = new PrismaClient({ adapter });
I kept the normal Neon hostname in my environment variables.
I did not need to hardcode an IP address.
12. Why This Problem Can Affect Other Developers
This is not necessarily a problem specific to my project.
Similar issues can occur in Node.js applications when:
- A hostname resolves to both IPv4 and IPv6 addresses.
- The local network has incomplete IPv6 connectivity.
- DNS resolvers behave inconsistently.
- A VPN, firewall, router, or ISP affects address-family connectivity.
- A runtime or database driver attempts connections using multiple resolved addresses.
For example, developers using:
- Neon PostgreSQL
- Other managed PostgreSQL services
- External APIs
- WebSocket connections
- Node.js applications running on dual-stack networks
may encounter similar symptoms.
However, this does not mean every Neon or Prisma developer needs this workaround.
It depends on the network and runtime environment.
13. Important Considerations Before Using This Fix in Production
Although this configuration solved my development issue, there are a few things worth considering.
Don't hardcode database IP addresses
The fixed-IP approach was useful for debugging, but it is not a good permanent production solution.
Database infrastructure can change IP addresses.
Keep using the hostname provided by your database provider.
Understand the global effect
These settings affect the Node.js process, not just the database connection.
In particular:
net.setDefaultAutoSelectFamily(false);
changes automatic address-family selection for other connections created in that process.
Test other outbound services after applying it.
Investigate your DNS configuration
My Windows DNS server did not respond to the tested DNS queries.
That is a separate network configuration issue worth investigating.
Test your production environment independently
A local Windows networking workaround may not be necessary on a Linux cloud server.
Always test the production environment before carrying this configuration into deployment.
Keep TLS verification enabled
When testing with a fixed IP, I preserved TLS hostname verification using the original Neon hostname.
Never disable certificate verification just to make a database connection work.
14. Lessons Learned
This investigation taught me a few useful debugging principles.
1. Don't assume a database error is a database problem.
A connection timeout can originate from DNS, routing, IPv6, TLS, a driver, or the application itself.
2. Test each layer independently.
I separated the investigation into:
- Windows networking
- HTTPS connectivity
- Node.js DNS resolution
- TCP connectivity
- WebSocket connectivity
- PostgreSQL connectivity
- Prisma adapter connectivity
This made it possible to identify where the behavior changed.
3. Reproduce failures outside the application framework.
Testing Prisma outside Next.js helped eliminate application routing and framework-specific issues.
4. Compare different connection methods.
Testing pg.Client and Prisma against the same database helped distinguish a driver configuration issue from a general database connectivity issue.
5. Use fixed IP addresses only for diagnosis.
Connecting through a known working IP was useful for proving that the database and Prisma were functional.
But the final application should retain the provider's hostname.
Conclusion
What initially looked like a Prisma or Neon connection problem turned out to be a networking compatibility issue involving IPv4, IPv6, DNS resolution, and Node.js address selection.
The most important evidence was that:
- All three Neon IPv4 endpoints worked.
- IPv6 connections were unavailable.
- Direct PostgreSQL queries worked through fixed IPv4 addresses.
- Prisma successfully executed SQL using the same fixed IPv4 connection.
- The application started working after changing Node.js address-selection settings.
The final configuration was:
dns.setDefaultResultOrder("ipv4first");
net.setDefaultAutoSelectFamily(false);
This resolved the issue in my local development environment.
The broader lesson is simple:
When a database connection times out, don't immediately blame the ORM or the database. Investigate the networking layers between your application and the database first.
Sometimes the problem isn't in your query, your database, or even your ORM. It's in how your runtime gets to the server.
Note: This article documents the behaviour observed in my Windows 11 and Node.js 22.22.2 environment. The diagnosis is based on controlled connection tests and a verified workaround. It should not be interpreted as a general Prisma or Neon defect.
About the author
- I build fast, user-friendly web applications that help teams ship better digital products. My focus is on responsive UI, clean architecture, and production-ready frontend systems. Over the past 2+ years, I’ve worked with React, Angular, and TypeScript building real-world applications, integrating APIs, and delivering reliable user experiences in production environments. I’ve also worked with headless CMS solutions like Strapi and Drupal using GraphQL to enable dynamic content and structured systems. I care about writing maintainable, component-driven code and optimizing performance across devices. This blog is where I document what I build, what breaks, and what actually works in real projects. Lately, I’m exploring the shift from writing code to thinking in terms of products understanding users, systems, and decisions behind what we build.
Comments
Join the discussionNo comments yet. Be the first to comment.

