QuotaGuard and Neon IP Allow Integration Guide
Table of contents
- The Problem: IP Allow Needs an Address Serverless Does Not Give You
- Native Neon Options (What They Cover)
- Getting Started
- Understand Neon’s Endpoint Routing Before You Tunnel
- Configuring Neon IP Allow
- Configuring Your Application
- Using QGTunnel
- Testing Your Implementation
- Latency Considerations
- Troubleshooting
This IP address is not allowed to connect to this endpoint- It works on one branch and fails on another
- TLS or certificate errors after adding the tunnel
endpoint ID not specifiedor a routing error- The IP Allow section is not available
password authentication failed- SOCKS authentication failed
ECONNREFUSED 127.0.0.1
- Environment Variables Reference
- Security Best Practices
- QuotaGuard Static vs QuotaGuard Shield
- Ready to Get Started?
QuotaGuard Static IPs let your cloud application connect to a Neon Postgres project that has IP Allow enabled. You add the two static IPs assigned to your subscription, and every connection arrives from a known address no matter which platform your app runs on.
You do not need QuotaGuard for local development or for infrastructure with stable IPs. This guide is for applications on Vercel, AWS Lambda, Render, Railway, Fly.io, Heroku, Google Cloud Run, Kubernetes, and other platforms where outbound IPs are dynamic.
The Problem: IP Allow Needs an Address Serverless Does Not Give You
Neon’s IP Allow feature limits which addresses can connect to your project. Neon’s documentation describes it as ensuring that only trusted IP addresses can reach the project where your database resides.
That is the control a security review asks for. It also collides directly with Neon’s own audience, because Neon is built for serverless and its users run on exactly the platforms that have no stable egress address.
When you connect from an address that is not on the list, Neon tells you plainly:
This IP address is not allowed to connect to this endpoint
That message is more helpful than most. It arrives from the Neon Proxy, which sits in front of your database and evaluates connections before they reach Postgres, so you get a clear rejection rather than a silent timeout.
The Common Workaround
The usual response is to leave IP Allow off entirely, or to enter a range wide enough to cover whatever your platform might use, which in practice means most of the internet.
Neon endpoints are publicly addressable, and a Postgres connection string is the entire credential. Without a network restriction, a leaked connection string is unrestricted access from anywhere.
The QuotaGuard Solution
Route your Neon connections through QuotaGuard’s SOCKS5 proxy. Traffic exits from one of the two static IP addresses assigned to your subscription. Add those two addresses to IP Allow.
- Network-level protection that actually works on a serverless platform
- The same two IPs cover every platform in your stack
- Adding a worker on Fly.io or moving off Vercel changes nothing on the Neon side
- A leaked connection string is no longer sufficient on its own
Native Neon Options (What They Cover)
| Option | Requirements | Best for |
|---|---|---|
| IP Allow | Neon Scale plan | Any application that can present a stable source address |
| Protected branches | Neon paid plans | Scoping restrictions to production while dev branches stay open |
| Private Networking | AWS, via PrivateLink | Teams already inside AWS who want to bypass the public internet entirely |
Two constraints are worth knowing before you start.
IP Allow is gated to the Neon Scale plan. If you are on Free or Launch, the feature is not available to you regardless of how you configure the connection.
Protected branches are available on paid plans, with the Launch plan supporting up to 2 and the Scale plan up to 5. This matters because of how IP Allow can be scoped, covered below.
Use QuotaGuard when:
- Your application runs on a platform with dynamic egress IPs you cannot enumerate
- You are on Vercel, Netlify, or a serverless runtime where no VPC option exists
- Your stack spans several platforms and you want one pair of entries for all of them
- You want IP Allow on today without an infrastructure project
- You need the same static identity for other destinations too, not just Neon
Getting Started
After creating a QuotaGuard account, you will be redirected to your dashboard, where you can find your proxy credentials and the two static IP addresses assigned to your subscription.
Choose the right proxy region. Match your QuotaGuard region to the region hosting your Neon project. Postgres pays the latency cost on connection setup and on every round trip.
QuotaGuard proxies run in 12 AWS regions:
| Region | Location |
|---|---|
| US-East-1 | N. Virginia |
| US-East-2 | Ohio |
| US-West-2 | Oregon |
| CA-Central-1 | Montreal |
| EU-West-1 | Ireland |
| EU-West-2 | London |
| EU-Central-1 | Frankfurt |
| AP-Northeast-1 | Tokyo |
| AP-Southeast-1 | Singapore |
| AP-Southeast-2 | Sydney |
| AP-South-1 | Mumbai |
| SA-East-1 | Sao Paulo |
Your Neon project’s region is shown on the project dashboard and is encoded in the endpoint hostname. Pick the matching QuotaGuard region, or the nearest available one, from the region list in your dashboard.
Your QuotaGuard SOCKS5 credentials look like this:
Host: <region>-static-01.quotaguard.com
Port: 1080
Username: your-username
Password: your-password
For example, us-east-static-01.quotaguard.com. Use the exact host and credentials shown in your QuotaGuard dashboard rather than assembling a hostname by hand.
Finding your static IPs: both addresses are shown in the QuotaGuard dashboard. Add both. The dashboard pair is the single source of truth. Do not resolve the proxy hostname to obtain an address, and do not add only one of the two.
Understand Neon’s Endpoint Routing Before You Tunnel
This is the section that saves you an afternoon, and it is where Neon differs most from other managed Postgres providers.
Neon puts a proxy in front of your database, and that proxy uses the hostname you connect to in order to work out which compute endpoint you mean. The hostname is not incidental. It is the routing information.
Neon documents a fallback for client libraries that cannot send SNI, using an options=endpoint%3D<endpoint-id> parameter on the connection string. Check Neon’s current connection documentation for the exact syntax before relying on it, because the workaround has changed over time.
The practical consequence: do not point a Neon connection at localhost. A QGTunnel mapping in non-transparent mode rewrites the host, which strips the routing information Neon needs and breaks TLS certificate verification at the same time. Use transparent mode, which preserves the original hostname and overrides DNS instead. See how QGTunnel works for what transparent mode does.
Pooled and Direct Endpoints
Neon offers a pooled endpoint backed by PgBouncer and a direct endpoint that bypasses it. The pooled hostname carries a -pooler suffix. Serverless applications should use the pooled endpoint, because direct connections from short-lived functions exhaust connection limits quickly.
IP Allow applies to the project, so both endpoints are covered. Your choice between them is about connection management, not about the allowlist.
Branches Multiply Hostnames
Each Neon branch gets its own endpoint and hostname. If you tunnel to a specific host, every branch you actually connect to needs its own mapping, and preview branches created per pull request will not have one.
Two ways to handle this. Scope IP Allow to protected branches only, so production is restricted and ephemeral branches stay open. Or use a wildcard host in your tunnel configuration; see wildcard host support.
Configuring Neon IP Allow
Step 1: Open Network Security
- Select your project in the Neon Console
- On the Project Dashboard, select Settings
- Select Network Security
Step 2: Add Your QuotaGuard IPs
Under IP Allow, enter the addresses you want to permit. Neon accepts individual IP addresses, ranges, and CIDR notation, with multiple entries separated by commas.
Add both static IPs from your QuotaGuard dashboard:
203.0.113.10, 203.0.113.11
Substitute the actual addresses shown in your dashboard.
Click Save changes.
Step 3: Decide Whether to Scope to Protected Branches
Neon offers a Restrict IP access to protected branches only option. With it enabled, your allowlist applies only to branches you have marked protected, and other branches carry no IP restriction.
This is usually the right setting for a team using Neon’s branching workflow. Production is locked to your QuotaGuard pair, while preview and development branches stay reachable from developer laptops and CI without anyone maintaining a list of home IP addresses.
If you want the restriction to apply everywhere, leave the option off.
Step 4: Verify Before You Rely on It
Confirm a successful connection through the proxy before you treat the restriction as your access control. Add the pair, deploy the proxy configuration, confirm the application connects, then finish locking down.
Configuring with the CLI
The Neon CLI manages the allowlist with the ip-allow command, which supports list, add, remove, and reset.
List what is currently configured:
neon ip-allow list
Add your QuotaGuard addresses:
neon ip-allow add 203.0.113.10 203.0.113.11
add appends to the existing list. reset replaces it entirely, and running reset with no addresses removes everything currently defined. Check Neon’s CLI reference for the current flags on your installed version before scripting this.
Configuring Your Application
Postgres uses its own wire protocol over TCP, so this needs a SOCKS5 proxy, not an HTTP proxy. QuotaGuard’s SOCKS5 service runs on port 1080. If you want the background on the difference, see HTTP vs SOCKS5.
Most Postgres drivers have no proxy option, because they wrap libpq or open a raw socket directly. Patching Python’s socket module with PySocks does not reach psycopg2, since libpq opens its socket in C and never passes through Python’s socket layer.
| Language or stack | Recommended approach |
|---|---|
Go (pgx, lib/pq) |
Direct SOCKS5 with a custom dialer |
Node.js (pg, Prisma, Drizzle, Sequelize) |
QGTunnel in transparent mode, or a custom stream on pg |
Python (psycopg2, psycopg, SQLAlchemy, Django) |
QGTunnel in transparent mode |
| Ruby (ActiveRecord, Sequel) | QGTunnel in transparent mode |
| Java (JDBC) | QGTunnel in transparent mode |
| PHP (PDO) | QGTunnel in transparent mode |
| Elixir (Ecto, Postgrex) | QGTunnel in transparent mode |
Go is the cleanest integration here, because a custom dialer leaves the hostname untouched, which is exactly what Neon’s routing needs.
Go with pgx
go get golang.org/x/net/proxy
package main
import (
"context"
"fmt"
"log"
"net"
"os"
"github.com/jackc/pgx/v5"
"golang.org/x/net/proxy"
)
func main() {
auth := &proxy.Auth{
User: os.Getenv("QUOTAGUARD_SOCKS_USER"),
Password: os.Getenv("QUOTAGUARD_SOCKS_PASS"),
}
socksAddr := fmt.Sprintf("%s:%s",
os.Getenv("QUOTAGUARD_SOCKS_HOST"),
os.Getenv("QUOTAGUARD_SOCKS_PORT"),
)
socksDialer, err := proxy.SOCKS5("tcp", socksAddr, auth, proxy.Direct)
if err != nil {
log.Fatal("socks5 dialer:", err)
}
config, err := pgx.ParseConfig(os.Getenv("DATABASE_URL"))
if err != nil {
log.Fatal("parse config:", err)
}
config.DialFunc = func(ctx context.Context, network, addr string) (net.Conn, error) {
return socksDialer.Dial(network, addr)
}
conn, err := pgx.ConnectConfig(context.Background(), config)
if err != nil {
log.Fatal("connect:", err)
}
defer conn.Close(context.Background())
var now string
if err := conn.QueryRow(context.Background(), "select now()::text").Scan(&now); err != nil {
log.Fatal("query:", err)
}
fmt.Println("Connected to Neon through QuotaGuard at", now)
}
The connection string keeps the real Neon hostname, so SNI routing and TLS verification both behave normally. Only the transport underneath changes.
Node.js with a custom stream on pg
The pg client accepts a stream factory, which lets you supply a socket opened through SOCKS5 while leaving the connection string intact.
npm install pg socks
const { Client } = require('pg');
const { SocksClient } = require('socks');
const { URL } = require('url');
const dbUrl = new URL(process.env.DATABASE_URL);
const client = new Client({
connectionString: process.env.DATABASE_URL,
ssl: { rejectUnauthorized: true },
stream: () => {
// socks returns a promise, so this requires a pg version that
// accepts an async stream factory. Verify against your pinned version.
return SocksClient.createConnection({
proxy: {
host: process.env.QUOTAGUARD_SOCKS_HOST,
port: Number(process.env.QUOTAGUARD_SOCKS_PORT),
type: 5,
userId: process.env.QUOTAGUARD_SOCKS_USER,
password: process.env.QUOTAGUARD_SOCKS_PASS,
},
command: 'connect',
destination: {
host: dbUrl.hostname,
port: Number(dbUrl.port || 5432),
},
}).then((info) => info.socket);
},
});
client.connect()
.then(() => client.query('select now()'))
.then((res) => console.log('Connected to Neon through QuotaGuard', res.rows[0]))
.catch((err) => console.error('Connection failed:', err));
Support for an asynchronous stream factory varies across pg releases. Confirm the behavior on the version you have pinned before relying on it in production.
Note that Neon’s serverless driver, which speaks HTTP rather than the Postgres wire protocol, does not use this path at all. If you use it, route it through the HTTP proxy on port 9293 instead of SOCKS5.
Using QGTunnel
QGTunnel maps traffic through the QuotaGuard proxy without any driver support. For Neon, use transparent mode, which overrides DNS for the Neon hostname and keeps your connection string unchanged. Non-transparent mode points your client at localhost, which breaks Neon’s endpoint routing and TLS verification together.
Step 1: Download QGTunnel
curl https://s3.amazonaws.com/quotaguard/qgtunnel-latest.tar.gz | tar xz
This creates bin/qgtunnel and supporting files under vendor/nss_wrapper/.
The binary targets Linux. If you develop on macOS or Windows, test tunnel configuration inside a Linux container or VM so your local environment matches production.
Step 2: Create the Tunnel in Your Dashboard
In your QuotaGuard dashboard, go to Setup (the gear icon, top-right) > QGTunnel Configuration > Create a Tunnel.
| Setting | Value |
|---|---|
| Remote Destination | tcp://<your-neon-endpoint-host>:5432 |
| Local Port | 5432 |
| Transparent | true |
| Encrypted | false |
Take the host from your Neon connection string. Neon connections carry their own TLS, so the tunnel’s encryption option is not needed.
Set transparent to true. This is the difference between a working Neon setup and an afternoon of certificate errors.
If you connect to several branches, consider a wildcard host rather than one mapping per branch. See wildcard host support.
Step 3: Download the Configuration File
Download the configuration file from the dashboard and save it as .qgtunnel in your project root, then commit it. With the file present, your application does not depend on reaching the QuotaGuard API at startup.
Step 4: Leave Your Connection String Alone
In transparent mode, no change is needed:
postgresql://<user>:<password>@<endpoint>.<region>.aws.neon.tech/neondb?sslmode=require
That is the point of transparent mode, and for Neon it is not a convenience but a requirement.
Step 5: Run Your Application Under QGTunnel
Local or generic:
bin/qgtunnel node server.js
Heroku (Procfile):
web: bin/qgtunnel node server.js
Render (start command):
bin/qgtunnel npm start
Railway (start command):
bin/qgtunnel npm start
Fly.io (fly.toml):
[processes]
app = "bin/qgtunnel npm start"
Docker:
ENTRYPOINT ["/app/bin/qgtunnel"]
CMD ["node", "server.js"]
Kubernetes (container spec):
command: ["/app/bin/qgtunnel"]
args: ["node", "server.js"]
QGTunnel must be the entrypoint so it is listening before your application opens its first connection.
Testing Your Implementation
Confirm the Source Address Neon Sees
select inet_client_addr();
Node.js:
client.query('select inet_client_addr() as source_ip')
.then((res) => console.log('Neon sees this source IP:', res.rows[0].source_ip));
Python:
cur.execute("select inet_client_addr()")
print("Neon sees this source IP:", cur.fetchone()[0])
The value should match one of the two addresses in your QuotaGuard dashboard. Run it several times and both should appear across repeated connections.
If Neon’s proxy layer masks the client address on your plan, use the independent check below instead.
Confirm the Outbound IP Separately
import os
import requests
proxy_url = os.environ["QUOTAGUARDSTATIC_URL"]
proxies = {"http": proxy_url, "https": proxy_url}
response = requests.get("https://ip.quotaguard.com", proxies=proxies)
print("Outbound IP:", response.json()["ip"])
A connection that succeeds while reporting an address not in your dashboard means the proxy configuration was not applied to that connection. A successful query is not by itself proof that traffic went through the proxy.
Latency Considerations
Routing through a proxy adds a hop to every database connection. Neon has one characteristic that interacts with this specifically.
Neon suspends compute when a project is idle and resumes it on the next connection. That resume already costs time on the first request after a quiet period, and a proxied connection setup sits on top of it. If your workload is bursty, the combined cold path is what your users feel.
To keep the impact small:
- Match all three regions. Application, QuotaGuard proxy, and Neon project in the same region wherever possible.
- Use the pooled endpoint. Serverless runtimes open and discard connections aggressively, which is the worst case for per-connection overhead through a proxy.
- Consider your compute suspend settings. If cold starts already matter to you, a longer idle timeout reduces how often you pay both costs together.
- Use connection pooling in your application too, so the driver holds connections rather than reconnecting per request.
Measure against your own workload and region pairing rather than assuming a fixed figure.
Troubleshooting
This IP address is not allowed to connect to this endpoint
Neon is telling you exactly what happened. In order:
- Confirm both QuotaGuard IPs are in the IP Allow list, not just one
- Confirm the addresses match your dashboard exactly
- Confirm Save changes was clicked in the Neon Console
- Confirm the connection is going through the proxy using the checks above
- Confirm you are using SOCKS5 on port 1080, not the HTTP proxy on 9293
It works on one branch and fails on another
Check whether Restrict IP access to protected branches only is enabled and which branches are marked protected. If it is enabled, unprotected branches carry no restriction, so a working dev branch tells you nothing about production. If it is disabled, every branch is restricted and every branch you tunnel to needs a mapping.
TLS or certificate errors after adding the tunnel
You are almost certainly in non-transparent mode, connecting to localhost while the certificate is issued for the Neon hostname. Switch to transparent mode. Do not disable certificate verification to work around it.
endpoint ID not specified or a routing error
Neon uses the connection hostname to route to the correct compute endpoint. Anything that rewrites the host, including a non-transparent tunnel, removes that information. Use transparent mode, or check Neon’s documentation for the current options=endpoint%3D connection parameter.
The IP Allow section is not available
Neon’s documentation states IP Allow is offered on the Scale plan. It is not available on lower plans.
password authentication failed
A Postgres credential error, not a proxy or allowlist problem. You reached the database. Check the role and password, and confirm you are using the credentials for the branch you are actually connecting to, since Neon can generate distinct role passwords per branch.
SOCKS authentication failed
- Check the SOCKS username and password against your dashboard
- Check for characters in the password that need URL encoding when embedded in a URL
- Confirm SOCKS5, not SOCKS4
ECONNREFUSED 127.0.0.1
QGTunnel is not listening. It either did not start, started after your application, or has no mapping for that host. Make QGTunnel the process entrypoint so it initializes first.
Environment Variables Reference
# SOCKS5 proxy (port 1080) - used for Postgres and other TCP connections
QUOTAGUARD_SOCKS_HOST=<region>-static-01.quotaguard.com
QUOTAGUARD_SOCKS_PORT=1080
QUOTAGUARD_SOCKS_USER=your-username
QUOTAGUARD_SOCKS_PASS=your-password
# HTTP proxy (port 9293) - used for HTTP and HTTPS requests,
# including Neon's HTTP serverless driver
QUOTAGUARDSTATIC_URL=http://username:password@<region>-static-01.quotaguard.com:9293
# Your Neon connection string, copied from the Neon Console
DATABASE_URL=postgresql://<user>:<password>@<endpoint>.<region>.aws.neon.tech/neondb?sslmode=require
Use the exact values from your QuotaGuard dashboard and your Neon Console.
Security Best Practices
- Turn IP Allow on and keep the list to your two addresses. A list wide enough to cover a whole cloud provider is not a restriction.
- Add both static IPs. Adding one produces intermittent failures that are hard to diagnose.
- Use protected branches to scope the restriction. Lock production, leave ephemeral preview branches open, and avoid maintaining a list of developer home addresses.
- Use per-branch roles. A connection string copied between branches means a leaked dev credential can reach production data.
- Never commit credentials. Use your platform’s secret storage for both the database URL and the proxy credentials.
- Rotate credentials periodically, on both the Neon and QuotaGuard sides.
QuotaGuard Static vs QuotaGuard Shield
| Feature | QuotaGuard Static | QuotaGuard Shield |
|---|---|---|
| Protocol | HTTP / HTTPS / SOCKS5 | HTTPS / SOCKS5 over TLS |
| Customer-to-proxy hop | Plaintext | TLS-encrypted |
| HTTPS payload | Tunneled end-to-end, never decrypted at the proxy | Tunneled end-to-end, never decrypted at the proxy |
| Best for | Most apps | Regulated data or environments that require TLS on every hop |
| Starting price (direct) | $19/month | $29/month |
Neon connections carry their own TLS, so the payload is protected in both cases. Static is right for most applications connecting to Neon. Choose Shield if the workload handles regulated data under HIPAA, PCI-DSS, or SOC 2, or if your environment requires TLS between your app and the proxy itself.
Ready to Get Started?
Turn on IP Allow without locking your own application out.